TSQL级联/瀑布值从当前行到下一行

时间:2013-02-14 14:47:29

标签: sql sql-server-2008 tsql common-table-expression

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

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中运行

谢谢!

1 个答案:

答案 0 :(得分: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; - )。