[简化]迄今为止的故事:SQL计算列,递归总结层次
在Visual Studio 2010下密度纤维板DB,我有如下表:
CREATE TABLE [dbo].[SandTable](
[id] [int] IDENTITY(1,1) NOT NULL,
[isDone] [bit] NOT NULL,
[percentComplete] AS ([dbo].[CompletePercent]([id],[isDone])),
[parentId] [int] NULL,
CONSTRAINT [PK_SandTable] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
ALTER TABLE [dbo].[SandTable] WITH CHECK ADD CONSTRAINT [FK_SandTable_SandTable] FOREIGN KEY([parentId])
的想法是该行形成为树/森林,其中parentId用作父节点的“指针”。
的“PERCENTCOMPLETE”计算列使用函数CompletePercent计算多少子树,在行扎根的,是完整的,如下:
- 如果某行的“isDone”位为1 ,那么我们认为整个子树是100%完成的(这是一个用户覆盖),所以返回1.0。
- 但是,如果'isDone'是0,我需要计算整个子树的'完整性'。我通过递归地平均直接孩子的“完整性”来做到这一点,这对他们的孩子是这样做的,等等直到叶子。
起初,我试着让'CompleteCommon'的平均值为直接子节点'percentComplete'列。但是,正如我发现的(后来在线确认),计算列不能用作计算列计算的一部分。
目前我通过始终得到1“isDone” = 1行; 0“isDone” = 0行,使用下面的实现CompletePercent的沮丧:
CREATE FUNCTION [dbo].[CompletePercent]
(
@id int,
@isDone bit = 0
)
RETURNS float
AS
BEGIN
DECLARE @result float
IF @isDone = 1
SET @result = 1.0
ELSE
SET @result =
(SELECT
CASE
WHEN (COUNT(*) = 0) THEN 0.0
ELSE AVG(dbo.CompletePercent(id, isDone))
END
FROM dbo.SandTable
WHERE parentId = @id
)
RETURN @result
END
我希望有简单的东西在这里,我只是失踪,从盯着它那么久。
我的下一步是尝试使用我正在研究的递归CTE。但是,我并不确定如何编码所需的“特殊”条件平均值。
如果任何人都可以在我的行为中发现错误,或引导我在CTE的方向,我会非常感激。
[编辑:]我已经来到了一个死胡同,即使在CTE的轨道,用下面的疯狂(也可能是浪费的,如果它可以运行)查询:
WITH Weights AS (SELECT SandTable.id, COUNT(NULLIF (SandTable.isDone, 0)) AS isDone, 100.0 AS weight, COUNT(ST.id) AS kids
FROM SandTable INNER JOIN
SandTable AS ST ON SandTable.id = ST.parentId
WHERE (SandTable.parentId IS NULL)
GROUP BY SandTable.id
UNION ALL
SELECT SandTable_1.id, COUNT(NULLIF (SandTable_1.isDone, 0)) AS isDone, MyCTE_2.weight/MyCTE_2.kids AS weight, COUNT(ST_1.id) AS kids
FROM SandTable AS SandTable_1 INNER JOIN
MyCTE AS MyCTE_2 ON SandTable_1.parentId = MyCTE_2.id AND MyCTE_2.isDone = 0 INNER JOIN
SandTable AS ST_1 ON SandTable.id = ST_1.parentId
WHERE (SandTable_1.parentId IS NOT NULL)
GROUP BY SandTable_1.id)
SELECT SUM(weight)
FROM Weights AS Weights_1
WHERE (isDone > 0)
当时的想法是沿着层次结构(目前来自根,但我计划修改它以一个特定的ID开始),并且为每个节点计算子节点的数量并测试'isDone'(在此处作为汇总计算JOIN用于执行计数,现在isDone在CTE结果中不是0时被认为是'真')。每个节点的“权重”(实际上它贡献给总数的百分比)是它的父权重除以其兄弟(包括它自己)的数量,并将根设置为100%。
对于'isDone'节点或树叶停止下行。两者都将有下一个递归步骤返回0行)。
最后,将'idDone'节点的总权重相加(其他节点仅用于递归)。
但是,由于产生的错误状态: “GROUP BY,HAVING或聚合函数不允许在递归公用表表达式中使用GROUP BY,HAVING或聚合函数。
此外,任何方向任何进展的任何暗示将不胜感激。
问候, ShaiB
您正在使用哪种SQL Server版本? (“* Visual Studio 2010 *”只是一个客户端应用程序,它不会告诉我们任何有关您的数据库的信息) – 2013-02-24 08:24:07
对不起,没有指定:我的MS SQL Server 2008 R2安装在我的开发机器上。我正在通过VS的服务器浏览器编辑所有内容。 – 2013-02-24 08:36:55