到目前为止的[简化]故事:
在Visual Studio 2010下的.mdf数据库中,我有一个下表:
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来计算以行为根的子树的完整程度,如下所示:
起初,我尝试将'CompletePercent'平均值设为直接子项'percentComplete'列。但是,正如我发现的(后来在线确认),计算列不能用作计算列计算的一部分。
目前,我很沮丧,因为'isDone'= 1行总是得1,而'isDone'= 0行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或聚合函数。”
同样,任何方向取得任何进展的任何提示都将受到高度赞赏。
此致 ShaiB
答案 0 :(得分:0)
无论您选择什么路线,这都可能是相当昂贵的操作。但是,这里有一些可能会有所帮助的想法:
首先,您是否考虑过使用视图?您可以将计算列放在表上并将其添加到视图中,这可能会让您绕过计算列约束。您还可以使视图可更新(通过代替触发器),这样对于您的应用程序,它的行为就像一个表。
其次,您可以通过存储过程来完成此操作。使用游标一次迭代基表一行,计算percentComplete
列的值,并将结果存储在表变量中。 (您可能会以这样的方式编写它,您只需要访问基表中的每一行。)然后只需返回(即,SELECT)表变量的结果。
第三,类似于第二种,在插入/更新/删除之后写入触发器以重新计算每行的percentComplete
,而不是使用计算列。虽然这会给你非常快速的读取,但写入时可能会非常慢。
第四,您可以通过CLR功能执行此操作(即,将其写入C#并将其导入服务器)。对于具有CLR功能的函数,您可以摒弃许多SQL Server(傻瓜)规则。 (虽然,这并不意味着它总是一个好主意。)
第五,也许最复杂的是,您可以编写一个CLR表函数来读取表中的行(没有percentComplete
)并计算并将percentComplete
列附加到结果集。然后,使用它作为视图的基础(即SELECT * FROM dbo.GetTheTree()
),然后使用替代触发器使视图可更新(类似于第二个选项)。
希望能给你一些想法!