递归CTE通过多个级别更新父记录

时间:2015-08-06 01:57:15

标签: sql-server common-table-expression recursive-query

CTE会在下一次递归中使用作为CTE一部分更新的数据吗?我试图尝试这个CTE,因为WHILE循环内部类似UPDATE逻辑的性能表现不佳,我希望使用CTE可以更好地设置并且性能更好。

我在使用递归CTE更新表时遇到问题,直到父行都被正确标记为止。

SQL Fiddle

SQL小提琴显示表格和基本CTE。在添加任何AND / OR或级别逻辑之前,我似乎无法让CTE爬上层次结构并将父母标记为"遇到"。

以下是示例表:

| LogicID   | ParentLogic   | Depth     | Type  | Description                           | Met   |
|---------  |-------------  |-------    |------ |-------------------------------------  |-----  |
| 1         | NULL          | NULL      | NULL  | Conditions All Met                    | 0     |
| 2         | 1             | 1         | AND   | The sky or ocean is blue              | 0     |
| 3         | 2             | 2         | OR    | The sky is blue                       | 0     |
| 4         | 2             | 2         | OR    | The ocean is blue                     | 1     |
| 5         | 1             | 1         | AND   | The grass is green or road is black   | 0     |
| 6         | 5             | 2         | OR    | The grass is green                    | 1     |
| 7         | 5             | 2         | OR    | The road is black                     | 0     |
| 8         | 1             | 1         | AND   | Birds, bugs or the 4 below            | 0     |
| 9         | 8             | 2         | OR    | There are birds                       | 0     |
| 10        | 8             | 2         | OR    | There are bugs                        | 0     |
| 11        | 8             | 2         | OR    | All 4 below                           | 0     |
| 12        | 11            | 3         | AND   | There are dogs                        | 1     |
| 13        | 11            | 3         | AND   | There are cats                        | 1     |
| 14        | 11            | 3         | AND   | There are people                      | 1     |
| 15        | 11            | 3         | AND   | There are chairs                      | 1     |
DROP TABLE MyLogic
CREATE TABLE MyLogic
    (
        LogicID int
        ,ParentLogic int
        ,Depth int
        ,Type varchar(4)
        ,Description varchar(35)
        ,Met int
    );

INSERT  INTO MyLogic
        ( LogicID, ParentLogic, Depth, Type, Description, Met )
VALUES
        ( 1, NULL, NULL, NULL, 'Conditions All Met', 0 ),
        ( 2, 1, 1, 'AND', 'The sky or ocean is blue', 0 ),
        ( 3, 2, 2, 'OR', 'The sky is blue', 0 ),
        ( 4, 2, 2, 'OR', 'The ocean is blue', 1 ),
        ( 5, 1, 1, 'AND', 'The grass is green or road is black', 0 ),
        ( 6, 5, 2, 'OR', 'The grass is green', 1 ),
        ( 7, 5, 2, 'OR', 'The road is black', 0 ),
        ( 8, 1, 1, 'AND', 'Birds, bugs or the 4 below', 0 ),
        ( 9, 8, 2, 'OR', 'There are birds', 0 ),
        ( 10, 8, 2, 'OR', 'There are bugs', 0 ),
        ( 11, 8, 2, 'OR', 'All 4 below', 0 ),
        ( 12, 11, 3, 'AND', 'There are dogs', 1 ),
        ( 13, 11, 3, 'AND', 'There are cats', 1 ),
        ( 14, 11, 3, 'AND', 'There are people', 1 ),
        ( 15, 11, 3, 'AND', 'There are chairs', 1 )

这只是一组更复杂的逻辑示例。基本上我的想法是我需要每一组孩子来“累积”#34;使用表中的逻辑到父级。深度可变,但很可能是7深。

因此,逻辑ID 12,13,14,15被AND在一起,然后将11标记为Met。然后评估9,10,11,如果有的话(OR)则将8标记为Met。依此类推,直到最高级别的父级逻辑ID 1要么遇到要么不符合。

这可以通过CTE来完成,如果是这样,有人可以帮助我实现吗?

EDIT :: 感谢您的帮助 - 这里要求的是更新声明。

DECLARE @maxdepth AS int = ( SELECT MAX (Depth) FROM MyLogic) DECLARE @counter AS int = 0 WHILE ( @counter < @maxdepth ) BEGIN UPDATE UP SET UP.Met = --SELECT *, CASE WHEN ORIG.Type = 'AND' AND ORIG.Met = 0 AND COUNTS.CountMet = 2 THEN 0 WHEN ORIG.Type = 'AND' AND ORIG.Met = 0 AND COUNTS.CountMet = 1 THEN 0 WHEN ORIG.Type = 'AND' AND ORIG.Met = 1 AND COUNTS.CountMet = 2 THEN 0 WHEN ORIG.Type = 'AND' AND ORIG.Met = 1 AND COUNTS.CountMet = 1 THEN 1 WHEN ORIG.Type = 'OR' AND ORIG.Met = 1 AND COUNTS.CountMet = 1 THEN 1 WHEN ORIG.Type = 'OR' AND ORIG.Met = 0 AND COUNTS.CountMet = 2 THEN 1 WHEN ORIG.Type = 'OR' AND ORIG.Met = 1 AND COUNTS.CountMet = 2 THEN 1 WHEN ORIG.Type = 'OR' AND ORIG.Met = 0 AND COUNTS.CountMet = 1 THEN 0 END FROM MyLogic UP INNER JOIN dbo.MyLogic ORIG ON UP.LogicID = ORIG.ParentLogic INNER JOIN ( SELECT DIST.ParentLogic ,COUNT(DISTINCT DIST.Met) AS CountMet FROM MyLogic DIST GROUP BY DIST.ParentLogic ) COUNTS ON ORIG.ParentLogic = COUNTS.ParentLogic SET @counter = @counter + 1 END

1 个答案:

答案 0 :(得分:0)

我不相信你能用CTE在这个场景中完成你的目标。我尝试使用CTE的每种方法都取决于能否在CTE的递归部分使用GROUP BY,这是不允许的。

这是一个可以加快更新速度的替代方案。它仍然有点迭代,但在我的快速测试中看起来好一点,主要是因为更多基于集合的方法和更少的连接需要。它应该比当前流程更有效地扩展。

DECLARE @D AS INT = ( SELECT MAX (Depth) FROM MyLogic)
WHILE @D > 0 BEGIN
    UPDATE P SET
        Met = C.Met
    FROM MyLogic P
        INNER JOIN (
            SELECT

                ParentLogic,
                CASE
                    WHEN SUM(CASE WHEN Type = 'OR' THEN Met ELSE 0 END) > 0
                        OR SUM(CASE WHEN Type = 'AND' THEN Met ELSE 0 END) = COUNT(*)
                    THEN 1 ELSE 0 END AS Met
            FROM MyLogic
            WHERE Depth = @D
            GROUP BY
                ParentLogic
        ) C -- Only update parents with children
            ON P.LogicID = C.ParentLogic
    WHERE COALESCE(P.Depth, 0) = @D - 1 -- Get next level up
    SET @D = @D - 1
END
SELECT * FROM MyLogic