2

我之前的编辑有点混乱。希望这可以清除它...

TL/DR——只需复制并执行这两个脚本块,它就会变得明显。


我有一个关于级联数据的问题。本质上,我试图根据一些预定义的条件(如下)以瀑布效应向下移动数据。我已经解决了 18 个场景中的 15 个,我帮助解决了剩下的 3 个场景,即 GID 为 9、10 和 18 的场景。

从某种角度来看,在我正在处理的系统中,数据会不断地导入系统中。数据稀疏,我正在努力重构一整套数据以完成导入过程。我几乎无法控制系统中数据的形状,或者提供给我的数据:-/

最终的问题是:我如何满足下面的 5 条级联规则,或者,我如何解决我在下面的脚本中提供的测试用例 #18?

级联规则
在这个简化的场景中,级联的“规则”如下:

  • 数据将仅在同一组内级联 ( GID)
  • 一组数据将从 1 ( Seq)开始排序
  • IsLive列将是 1 或 0
  • 如果IsLive = 1然后将数据向下移动,直到遇到另一个IsLive = 1IsLive = 0具有非空值
  • 如果IsLive = 0然后将数据向下移动到行中,直到您IsLive = 0用值击中另一个。

注意:我的脚本是一个简化的示例,但在完整场景中N,我需要在其上级联列。

解决方案说明
如果您运行下面的 SQL,您将看到 3 列,输入、输出 - CTE 的结果、预期 - 预期结果和结果 - 通过/失败。我包含了一个脚本,它既可以创建示例表,也可以通过执行简单地说明测试用例。

  • 下面的测试用例脚本有示例数据
  • 测试用例脚本有一个我为正确的期望值附加的列。(在 INSERT 脚本中查找 GID=18。)

我希望有人可以提供帮助,否则我可能不得不求助于 SQL CLR SP 解决方案。另外,我不依赖这个解决方案,你也可以完全放弃我的解决方案并想出一些新的东西。

测试用例

DECLARE @Test TABLE (GID int, Seq int, IsLive bit, 
                     Eff date, 
                     Name varchar(50), 
                     Expected varchar(50)) -- expected val should help debug!

INSERT INTO @Test VALUES (1, 1, 1, '01-08-2012', 'RTS', 'RTS')
INSERT INTO @Test VALUES (1, 2, 0, '01-09-2012', 'RTA', 'RTA')
INSERT INTO @Test VALUES (1, 3, 1, '01-10-2012', 'FSA', 'RTA')
INSERT INTO @Test VALUES (1, 4, 0, '01-11-2012',  NULL, 'RTA')
INSERT INTO @Test VALUES (1, 5, 1, '01-12-2012', 'FSA', 'RTA')
INSERT INTO @Test VALUES (2, 1, 1, '01-08-2012', 'RTS', 'RTS')
INSERT INTO @Test VALUES (2, 2, 0, '01-09-2012', 'RTA', 'RTA')
INSERT INTO @Test VALUES (2, 3, 1, '01-10-2012', 'FSA', 'RTA')
INSERT INTO @Test VALUES (2, 4, 0, '01-11-2012', 'GSM', 'GSM')
INSERT INTO @Test VALUES (2, 5, 1, '01-12-2012', 'FSA', 'GSM')
INSERT INTO @Test VALUES (3, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO @Test VALUES (3, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO @Test VALUES (4, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (4, 2, 0, '01-02-2012', 'FSA', 'FSA')
INSERT INTO @Test VALUES (4, 3, 0, '01-03-2012', NULL, 'FSA')
INSERT INTO @Test VALUES (5, 1, 0, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (5, 2, 1, '01-02-2012', 'LSI', 'LSI')
INSERT INTO @Test VALUES (5, 3, 0, '01-03-2012', NULL, 'LSI')
INSERT INTO @Test VALUES (6, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (6, 2, 0, '01-02-2012', 'LSI', 'LSI')
INSERT INTO @Test VALUES (6, 3, 1, '01-03-2012', NULL, 'LSI')
INSERT INTO @Test VALUES (7, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO @Test VALUES (7, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO @Test VALUES (7, 3, 1, '01-03-2012', 'RTA', 'RTA')
INSERT INTO @Test VALUES (8, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO @Test VALUES (8, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO @Test VALUES (8, 3, 1, '01-03-2012', NULL, NULL)
INSERT INTO @Test VALUES (9, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO @Test VALUES (9, 2, 1, '01-02-2012', NULL, NULL)
INSERT INTO @Test VALUES (9, 3, 1, '01-03-2012', 'RTS', 'RTS')
INSERT INTO @Test VALUES (10, 1, 1, '01-01-2012', 'FSA','FSA')
INSERT INTO @Test VALUES (10, 2, 1, '01-02-2012', 'GSM','GSM')
INSERT INTO @Test VALUES (10, 3, 1, '01-03-2012', 'RTS','RTS')  
INSERT INTO @Test VALUES (11, 1, 0, '01-01-2012', 'NOP','NOP')
INSERT INTO @Test VALUES (11, 2, 1, '01-02-2012', 'TAP','NOP')
INSERT INTO @Test VALUES (11, 3, 1, '01-03-2012', 'STG','NOP')
INSERT INTO @Test VALUES (12, 1, 1, '01-01-2012', 'RTS','RTS')
INSERT INTO @Test VALUES (12, 2, 0, '01-02-2012', 'RTM','RTM')
INSERT INTO @Test VALUES (12, 3, 1, '01-03-2012', 'LSA','RTM')
INSERT INTO @Test VALUES (12, 4, 1, '01-03-2012', 'LSA','RTM')
INSERT INTO @Test VALUES (12, 5, 1, '01-03-2012', 'GSM','RTM')
INSERT INTO @Test VALUES (13, 1, 1, '01-08-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (13, 2, 0, '01-09-2012', NULL, 'BAR')
INSERT INTO @Test VALUES (13, 3, 1, '01-10-2012', 'TST','TST')
INSERT INTO @Test VALUES (14, 1, 1, '01-08-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (14, 2, 0, '01-09-2012', 'GIP','GIP')
INSERT INTO @Test VALUES (14, 3, 1, '01-10-2012', 'TST','GIP')
INSERT INTO @Test VALUES (15, 1, 1, '01-01-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (15, 2, 0, '01-02-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (15, 3, 1, '01-02-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (15, 4, 1, '01-02-2012', 'GYM','BAR')
INSERT INTO @Test VALUES (16, 1, 1, '01-02-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (16, 2, 0, '01-03-2012', NULL, 'BAR')
INSERT INTO @Test VALUES (16, 3, 1, '01-03-2012', 'BAR','BAR')
INSERT INTO @Test VALUES (16, 4, 1, '01-03-2012', 'GYM','GYM')
INSERT INTO @Test VALUES (17, 1, 1, '01-02-2012', 'BAR', 'BAR')
INSERT INTO @Test VALUES (17, 2, 0, '01-03-2012', 'GIP', 'GIP')
INSERT INTO @Test VALUES (17, 3, 0, '01-03-2012', NULL,  'GIP') 
INSERT INTO @Test VALUES (17, 4, 1, '01-03-2012', 'TST', 'GIP') 

-- -------------------------------------------
-- Following is the GID=18 test case that fails
-- -------------------------------------------
INSERT INTO @Test VALUES (18, 1, 1, '01-02-2012', 'BAR', 'BAR')
INSERT INTO @Test VALUES (18, 2, 0, '01-03-2012', 'BAR', 'BAR')
INSERT INTO @Test VALUES (18, 3, 0, '01-03-2012', NULL, 'BAR') 
INSERT INTO @Test VALUES (18, 4, 1, '01-03-2012', 'TST', 'BAR') 

解决方案

DECLARE @PrevNonLiveSeq int = NULL

;WITH CTE AS ( 
    SELECT  T.GID, T.SEQ, T.IsLive, Expected
            , Name AS Name
            , CASE WHEN T.IsLive = 0 THEN T.SEQ ELSE NULL END As PrevNonLiveSeq
            , CASE WHEN T.IsLive = 1 THEN T.SEQ ELSE NULL END As PrevLiveSeq
            , NULL AS PerNonLiveSeqCalc
            , NULL AS PerLiveSeqCalc
            , 0 PrevSeq
            , CAST(NULL AS varchar(50)) PrevName
    FROM    @Test T
    WHERE   T.Seq = 1
    UNION ALL 
    SELECT  Curr.GID, Curr.SEQ, Curr.IsLive, Curr.Expected
            ,CASE   WHEN Curr.IsLive = 0 THEN ISNULL(Curr.Name, Prev.Name) 
                    ELSE CASE   WHEN PrevNonLive.Name IS NULL THEN 
                                    CASE WHEN Prev.Name <> PrevLive.Name THEN Prev.Name ELSE Curr.Name END
                                ELSE Prev.Name END
             END

            ,CASE WHEN Curr.IsLive = 0 THEN Curr.SEQ ELSE Prev.PrevNonLiveSeq END As PrevNonLiveSeq
            ,CASE WHEN Curr.IsLive = 1 THEN Curr.SEQ ELSE Prev.PrevLiveSeq END As PrevLiveSeq
            , ISNULL(Prev.PrevNonLiveSeq, Curr.SEQ) AS PerNonLiveSeqCalc
            , ISNULL(Prev.PrevLiveSeq, Curr.SEQ) AS PerLiveSeqCalc
            , Prev.Seq PrevSeq, Prev.Name PrevName
    FROM    CTE Prev 
    JOIN    @Test Curr ON Curr.GID = Prev.GID AND Curr.SEQ = Prev.SEQ+1
    JOIN    @Test PrevNonLive ON Prev.GID = PrevNonLive.GID AND PrevNonLive.SEQ = ISNULL(Prev.PrevNonLiveSeq, Curr.SEQ)
    JOIN    @Test PrevLive ON Prev.GID = PrevLive.GID AND PrevLive.SEQ = ISNULL(Prev.PrevLiveSeq, Curr.SEQ)
) 
SELECT CTE.GID, CTE.Seq, T.IsLive
        , T.Name Input, CTE.Name [Output]
        , CASE WHEN CTE.Name = CTE.Expected OR (CTE.Name IS NULL AND CTE.Expected IS NULL) THEN 'Pass' ELSE 'FAIL' END AS Result
        , CTE.Expected
FROM CTE 
INNER JOIN @Test T on CTE.GID = T.GID AND CTE.Seq = T.Seq
ORDER BY CTE.GID, CTE.Seq

结果

对于结果,请复制并在 SSMS 中运行

谢谢!

4

1 回答 1

1

这应该可以工作并且不需要递归 CTE。您只需要为您想要“级联”的每个实际字段执行 COALESCE。

SELECT crrnt.*, COALESCE(cscd.Name, crrnt.Name) AS [Output]
FROM @Test crrnt
OUTER APPLY (
            SELECT  TOP 1 *
            FROM    @Test prir
            WHERE   prir.GID = crrnt.GID
            AND     prir.Seq < crrnt.Seq
            AND     (
                        (
                            crrnt.IsLive = 1
                        AND prir.IsLive = 0
                        AND prir.Name IS NOT NULL
                        )
                    OR  (
                            crrnt.IsLive = 0 
                        AND crrnt.Name IS NULL
                        AND (
                                (
                                    prir.IsLive = 0
                                AND prir.Name IS NOT NULL
                                )
                            OR  (
                                    prir.IsLive = 1
                                AND NOT EXISTS(
                                                SELECT  *
                                                FROM    @Test confirm
                                                WHERE   confirm.GID = prir.GID
                                                AND     confirm.Seq < prir.Seq
                                                AND     confirm.IsLive = 0
                                                AND     confirm.Name IS NOT NULL
                                            )
                                )
                            )
                        )
                    )
            ORDER BY    prir.Seq DESC
            ) cscd

编辑:
测试查询的性能通常是一个好主意,所以下面就是这样。测试包括:
1. 从最初发布的查询和示例数据开始
2. 将临时变量更改为临时表(查询将最终命中真实用户表)
3. 在临时表上创建聚集索引,即:GID、Seq。
4. 复制数据,但 GID 值更高(将 18 行变成 6,300,063 行)
5. 使用 DBCC FREEPROCCACAHE 和 DBCC DROPCLEANBUFFERS 确保相同的环境
6. 使用 STATISTICS IO 和 STATISTICS TIME

SET NOCOUNT ON
-- DROP TABLE #Test
IF (OBJECT_ID('tempdb.dbo.#Test') IS NULL)
BEGIN
    CREATE TABLE #Test (GID INT NOT NULL, Seq INT NOT NULL, IsLive BIT NOT NULL, 
                     Eff date, 
                     Name varchar(50), 
                     Expected varchar(50), -- expected val should help debug!
                     PRIMARY KEY(GID, Seq)
                     )

INSERT INTO #Test VALUES (1, 1, 1, '01-08-2012', 'RTS', 'RTS')
INSERT INTO #Test VALUES (1, 2, 0, '01-09-2012', 'RTA', 'RTA')
INSERT INTO #Test VALUES (1, 3, 1, '01-10-2012', 'FSA', 'RTA')
INSERT INTO #Test VALUES (1, 4, 0, '01-11-2012',  NULL, 'RTA')
INSERT INTO #Test VALUES (1, 5, 1, '01-12-2012', 'FSA', 'RTA')
INSERT INTO #Test VALUES (2, 1, 1, '01-08-2012', 'RTS', 'RTS')
INSERT INTO #Test VALUES (2, 2, 0, '01-09-2012', 'RTA', 'RTA')
INSERT INTO #Test VALUES (2, 3, 1, '01-10-2012', 'FSA', 'RTA')
INSERT INTO #Test VALUES (2, 4, 0, '01-11-2012', 'GSM', 'GSM')
INSERT INTO #Test VALUES (2, 5, 1, '01-12-2012', 'FSA', 'GSM')
INSERT INTO #Test VALUES (3, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO #Test VALUES (3, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO #Test VALUES (4, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO #Test VALUES (4, 2, 0, '01-02-2012', 'FSA', 'FSA')
INSERT INTO #Test VALUES (4, 3, 0, '01-03-2012', NULL, 'FSA')
INSERT INTO #Test VALUES (5, 1, 0, '01-01-2012', NULL, NULL)
INSERT INTO #Test VALUES (5, 2, 1, '01-02-2012', 'LSI', 'LSI')
INSERT INTO #Test VALUES (5, 3, 0, '01-03-2012', NULL, 'LSI')
INSERT INTO #Test VALUES (6, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO #Test VALUES (6, 2, 0, '01-02-2012', 'LSI', 'LSI')
INSERT INTO #Test VALUES (6, 3, 1, '01-03-2012', NULL, 'LSI')
INSERT INTO #Test VALUES (7, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO #Test VALUES (7, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO #Test VALUES (7, 3, 1, '01-03-2012', 'RTA', 'RTA')
INSERT INTO #Test VALUES (8, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO #Test VALUES (8, 2, 0, '01-02-2012', NULL, 'FSA')
INSERT INTO #Test VALUES (8, 3, 1, '01-03-2012', NULL, NULL)
INSERT INTO #Test VALUES (9, 1, 1, '01-01-2012', 'FSA', 'FSA')
INSERT INTO #Test VALUES (9, 2, 1, '01-02-2012', NULL, NULL)
INSERT INTO #Test VALUES (9, 3, 1, '01-03-2012', 'RTS', 'RTS')
INSERT INTO #Test VALUES (10, 1, 1, '01-01-2012', 'FSA','FSA')
INSERT INTO #Test VALUES (10, 2, 1, '01-02-2012', 'GSM','GSM')
INSERT INTO #Test VALUES (10, 3, 1, '01-03-2012', 'RTS','RTS')  
INSERT INTO #Test VALUES (11, 1, 0, '01-01-2012', 'NOP','NOP')
INSERT INTO #Test VALUES (11, 2, 1, '01-02-2012', 'TAP','NOP')
INSERT INTO #Test VALUES (11, 3, 1, '01-03-2012', 'STG','NOP')
INSERT INTO #Test VALUES (12, 1, 1, '01-01-2012', 'RTS','RTS')
INSERT INTO #Test VALUES (12, 2, 0, '01-02-2012', 'RTM','RTM')
INSERT INTO #Test VALUES (12, 3, 1, '01-03-2012', 'LSA','RTM')
INSERT INTO #Test VALUES (12, 4, 1, '01-03-2012', 'LSA','RTM')
INSERT INTO #Test VALUES (12, 5, 1, '01-03-2012', 'GSM','RTM')
INSERT INTO #Test VALUES (13, 1, 1, '01-08-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (13, 2, 0, '01-09-2012', NULL, 'BAR')
INSERT INTO #Test VALUES (13, 3, 1, '01-10-2012', 'TST','TST')
INSERT INTO #Test VALUES (14, 1, 1, '01-08-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (14, 2, 0, '01-09-2012', 'GIP','GIP')
INSERT INTO #Test VALUES (14, 3, 1, '01-10-2012', 'TST','GIP')
INSERT INTO #Test VALUES (15, 1, 1, '01-01-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (15, 2, 0, '01-02-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (15, 3, 1, '01-02-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (15, 4, 1, '01-02-2012', 'GYM','BAR')
INSERT INTO #Test VALUES (16, 1, 1, '01-02-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (16, 2, 0, '01-03-2012', NULL, 'BAR')
INSERT INTO #Test VALUES (16, 3, 1, '01-03-2012', 'BAR','BAR')
INSERT INTO #Test VALUES (16, 4, 1, '01-03-2012', 'GYM','GYM')
INSERT INTO #Test VALUES (17, 1, 1, '01-02-2012', 'BAR', 'BAR')
INSERT INTO #Test VALUES (17, 2, 0, '01-03-2012', 'GIP', 'GIP')
INSERT INTO #Test VALUES (17, 3, 0, '01-03-2012', NULL,  'GIP') 
INSERT INTO #Test VALUES (17, 4, 1, '01-03-2012', 'TST', 'GIP') 

-- -------------------------------------------
-- Following is the GID=18 test case that fails
-- -------------------------------------------
INSERT INTO #Test VALUES (18, 1, 1, '01-02-2012', 'BAR', 'BAR')
INSERT INTO #Test VALUES (18, 2, 0, '01-03-2012', 'BAR', 'BAR')
INSERT INTO #Test VALUES (18, 3, 0, '01-03-2012', NULL, 'BAR') 
INSERT INTO #Test VALUES (18, 4, 1, '01-03-2012', 'TST', 'BAR') 

CHECKPOINT

INSERT INTO #Test (GID, Seq, IsLive, Eff, Name, Expected)
    SELECT  tmp.GID + (multiplier.Num * 20) AS [GID], tmp.Seq, tmp.IsLive, tmp.Eff, tmp.Name, tmp.Expected
    FROM    #Test tmp
    CROSS JOIN (
                    SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [Num]
                    FROM    master.sys.objects so1
                    CROSS JOIN  master.sys.objects so2
                    CROSS JOIN  master.sys.objects so3
                ) multiplier
    WHERE   multiplier.Num <= 100000

CHECKPOINT

SELECT COUNT(*) FROM #Test

ALTER INDEX ALL ON #Test REBUILD

-- SELECT TOP 1000 * FROM #Test ORDER BY GID, Seq

END /* IF (OBJECT_ID('tempdb.dbo.#Test') IS NULL) */
-----------------------------------------------------------------------------

DBCC FREEPROCCACHE WITH NO_INFOMSGS
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS

PRINT '-- Original solution (Recursive CTE):'
PRINT ''

SET STATISTICS IO ON
SET STATISTICS TIME ON

;WITH CTE AS ( 
    SELECT  T.GID, T.SEQ, T.IsLive, Expected
            , Name AS Name
            , CASE WHEN T.IsLive = 0 THEN T.SEQ ELSE NULL END As PrevNonLiveSeq
            , CASE WHEN T.IsLive = 1 THEN T.SEQ ELSE NULL END As PrevLiveSeq
            , NULL AS PerNonLiveSeqCalc
            , NULL AS PerLiveSeqCalc
            , 0 PrevSeq
            , CAST(NULL AS varchar(50)) PrevName
    FROM    #Test T
    WHERE   T.Seq = 1
    UNION ALL 
    SELECT  Curr.GID, Curr.SEQ, Curr.IsLive, Curr.Expected
            ,CASE   WHEN Curr.IsLive = 0 THEN ISNULL(Curr.Name, Prev.Name) 
                    ELSE CASE   WHEN PrevNonLive.Name IS NULL THEN 
                                    CASE WHEN Prev.Name <> PrevLive.Name THEN Prev.Name ELSE Curr.Name END
                                ELSE Prev.Name END
             END

            ,CASE WHEN Curr.IsLive = 0 THEN Curr.SEQ ELSE Prev.PrevNonLiveSeq END As PrevNonLiveSeq
            ,CASE WHEN Curr.IsLive = 1 THEN Curr.SEQ ELSE Prev.PrevLiveSeq END As PrevLiveSeq
            , ISNULL(Prev.PrevNonLiveSeq, Curr.SEQ) AS PerNonLiveSeqCalc
            , ISNULL(Prev.PrevLiveSeq, Curr.SEQ) AS PerLiveSeqCalc
            , Prev.Seq PrevSeq, Prev.Name PrevName
    FROM    CTE Prev 
    JOIN    #Test Curr ON Curr.GID = Prev.GID AND Curr.SEQ = Prev.SEQ+1
    JOIN    #Test PrevNonLive ON Prev.GID = PrevNonLive.GID AND PrevNonLive.SEQ = ISNULL(Prev.PrevNonLiveSeq, Curr.SEQ)
    JOIN    #Test PrevLive ON Prev.GID = PrevLive.GID AND PrevLive.SEQ = ISNULL(Prev.PrevLiveSeq, Curr.SEQ)
) 
SELECT CTE.GID, CTE.Seq, T.IsLive
        , T.Name Input, CTE.Name [Output]
        , CASE WHEN CTE.Name = CTE.Expected OR (CTE.Name IS NULL AND CTE.Expected IS NULL) THEN 'Pass' ELSE 'FAIL' END AS Result
        , CTE.Expected
FROM CTE 
INNER JOIN #Test T on CTE.GID = T.GID AND CTE.Seq = T.Seq
ORDER BY CTE.GID, CTE.Seq

SET STATISTICS TIME OFF
SET STATISTICS IO OFF

PRINT '=================================================='
------------------------------------------------------

DBCC FREEPROCCACHE WITH NO_INFOMSGS
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS

PRINT '-- Proposed solution (OUTER APPLY):'
PRINT ''

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT crrnt.GID, crrnt.Seq, crrnt.IsLive,
        COALESCE(cscd.Name, crrnt.Name) AS [Output],
        CASE
            WHEN COALESCE(COALESCE(cscd.Name, crrnt.Name), '~~~') = COALESCE(crrnt.Expected, '~~~') THEN 'Pass'
            ELSE 'FAIL'
        END AS [Result],
        crrnt.Expected
FROM #Test crrnt
OUTER APPLY (
            SELECT  TOP 1 *
            FROM    #Test prir
            WHERE   prir.GID = crrnt.GID
            AND     prir.Seq < crrnt.Seq
            AND     (
                        (
                            crrnt.IsLive = 1
                        AND prir.IsLive = 0
                        AND prir.Name IS NOT NULL
                        )
                    OR  (
                            crrnt.IsLive = 0 
                        AND crrnt.Name IS NULL
                        AND (
                                (
                                    prir.IsLive = 0
                                AND prir.Name IS NOT NULL
                                )
                            OR  (
                                    prir.IsLive = 1
                                AND NOT EXISTS(
                                                SELECT  *
                                                FROM    #Test confirm
                                                WHERE   confirm.GID = prir.GID
                                                AND     confirm.Seq < prir.Seq
                                                AND     confirm.IsLive = 0
                                                AND     confirm.Name IS NOT NULL
                                            )
                                )
                            )
                        )
                    )
            ORDER BY    prir.Seq DESC
            ) cscd

SET STATISTICS TIME OFF
SET STATISTICS IO OFF
-----------------------------------

我对上述测试的执行表明:

  • 原始查询:CPU 时间 = 173031 毫秒,经过时间 = 252708 毫秒,逻辑读取 = 97,538,739
  • 提议的查询 = CPU 时间 = 49125 毫秒,经过的时间 = 74003 毫秒,逻辑读取 = 17,747,775

因此,原始查询的 CPU 和运行时间都慢了大约 3.5 倍,逻辑读取量大约是我提出的查询的 5 倍。小心递归 CTE ;-)。

于 2013-02-15T08:00:06.317 回答