【发布时间】:2013-02-09 13:59:12
【问题描述】:
到目前为止的[简化]故事:
在 Visual Studio 2010 下的 .mdf 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,我需要计算整个子树的“完整性”。我通过递归平均直接孩子的“完整性”来做到这一点,直接孩子这样做是为了他们的孩子,依此类推,直到叶子。
起初,我尝试让“CompletePercent”平均直接孩子的“percentComplete”列。但是,正如我发现的(后来在网上确认),计算列不能用作计算列计算的一部分。
目前,使用 CompletePercent 的以下实现,对于 'isDone'=1 行总是得到 1,对于 'isDone'=0 行总是得到 0,这让我感到很沮丧:
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 不为 0,则在 CTE 的结果中将其视为“真”)。每个节点的“权重”(实际上是它占总数的百分比)是其父节点的权重除以其兄弟节点(包括自身)的数量,根集为 100%。
对于“isDone”节点或叶节点,停止行程。两者都会让下一个递归步骤返回 0 行)。
最后,将“idDone”节点的总权重相加(其他节点仅用于递归)。
但是,这无法运行,因为结果错误表明: “在递归公用表表达式的递归部分中不允许使用 GROUP BY、HAVING 或聚合函数”。
再次,我们将不胜感激任何有关在任何方向取得进展的提示。
问候, 谢B
【问题讨论】:
-
您使用的是哪个 SQL Server 版本? (“Visual Studio 2010”只是一个客户端应用程序,它不会告诉我们有关您的数据库的任何信息)
-
抱歉没有指定这个:我的开发机器上安装了 MS SQL Server 2008 R2。我正在通过 VS 的服务器资源管理器编辑所有内容。
标签: sql-server-2008-r2 common-table-expression recursive-query calculated-columns