3

这个问题是我的旧问题here的变体。我希望用一个例子来解释这个问题。所以

示例数据
这是要使用的示例数据:

DECLARE @Test TABLE (GID      int,             Seq     int, 
                     IsLive   bit,             Eff     date, 
                     Name     varchar(50),     Salary  decimal) 

INSERT INTO @Test VALUES (1, 1, 1, '01-08-2012', 'RTS', NULL)
INSERT INTO @Test VALUES (1, 2, 0, '01-09-2012', 'RTA', NULL)
INSERT INTO @Test VALUES (1, 3, 1, '01-10-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (1, 4, 0, '01-11-2012',  NULL, NULL)
INSERT INTO @Test VALUES (1, 5, 1, '01-12-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (2, 1, 1, '01-08-2012', 'RTS', NULL)
INSERT INTO @Test VALUES (2, 2, 0, '01-09-2012', 'RTA', NULL)
INSERT INTO @Test VALUES (2, 3, 1, '01-10-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (2, 4, 0, '01-11-2012', 'GSM', NULL)
INSERT INTO @Test VALUES (2, 5, 1, '01-12-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (3, 1, 1, '01-01-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (3, 2, 0, '01-02-2012', NULL, NULL)
INSERT INTO @Test VALUES (4, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (4, 2, 0, '01-02-2012', 'FSA', NULL)
INSERT INTO @Test VALUES (4, 3, 0, '01-03-2012', NULL, NULL)
INSERT INTO @Test VALUES (5, 1, 0, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (5, 2, 1, '01-02-2012', 'LSI', NULL)
INSERT INTO @Test VALUES (5, 3, 0, '01-03-2012', NULL, NULL)
INSERT INTO @Test VALUES (6, 1, 1, '01-01-2012', NULL, NULL)
INSERT INTO @Test VALUES (6, 2, 0, '01-02-2012', 'LSI', NULL)
INSERT INTO @Test VALUES (6, 3, 1, '01-03-2012', NULL, NULL)

SELECT * FROM @Test

以下是两个示例结果集。尽管片段显示了插入,但重点是显示可接受的输出集是什么样的:

示例输出 #1
在下面的数据集中,当行具有IsLive=0然后其列中的值必须覆盖其IsLive=1下方跳过 NULL 值的行上的相同列的值。忽略IsLive=1第一行之前的任何行IsLive=0

    INSERT INTO @Test VALUES (1, 1, 1, '01-08-2012', 'RTS', NULL)
    INSERT INTO @Test VALUES (1, 2, 0, '01-09-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (1, 3, 1, '01-10-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (1, 4, 0, '01-11-2012',  NULL, NULL)
    INSERT INTO @Test VALUES (1, 5, 1, '01-12-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 1, 1, '01-08-2012', 'RTS', NULL)
    INSERT INTO @Test VALUES (2, 2, 0, '01-09-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 3, 1, '01-10-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 4, 0, '01-11-2012', 'GSM', NULL)
    INSERT INTO @Test VALUES (2, 5, 1, '01-12-2012', 'GSM', NULL)
    INSERT INTO @Test VALUES (3, 1, 1, '01-01-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (3, 2, 0, '01-02-2012', NULL, NULL)
    INSERT INTO @Test VALUES (4, 1, 1, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (4, 2, 0, '01-02-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (4, 3, 0, '01-03-2012', NULL, NULL)
    INSERT INTO @Test VALUES (5, 1, 0, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (5, 2, 1, '01-02-2012', 'LSI', NULL)
    INSERT INTO @Test VALUES (5, 3, 0, '01-03-2012', NULL, NULL)
    INSERT INTO @Test VALUES (6, 1, 1, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (6, 2, 0, '01-02-2012', 'LSI', NULL)
    INSERT INTO @Test VALUES (6, 3, 1, '01-03-2012', 'LSI', NULL)

    SELECT * FROM @Test AS FakedOutput_1

示例输出 #2
在下面的数据集中,当一行具有IsLive=0然后其列中的值必须覆盖其下方行上相同列的值IsLive=1。具有 NULL 值的列采用前一行的值。

    INSERT INTO @Test VALUES (1, 1, 1, '01-08-2012', 'RTS', NULL)
    INSERT INTO @Test VALUES (1, 2, 0, '01-09-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (1, 3, 1, '01-10-2012', 'RTA', NULL)
    -- <- the following row is different from prev
    INSERT INTO @Test VALUES (1, 4, 0, '01-11-2012', 'RTA', NULL) 
    INSERT INTO @Test VALUES (1, 5, 1, '01-12-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 1, 1, '01-08-2012', 'RTS', NULL)
    INSERT INTO @Test VALUES (2, 2, 0, '01-09-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 3, 1, '01-10-2012', 'RTA', NULL)
    INSERT INTO @Test VALUES (2, 4, 0, '01-11-2012', 'GSM', NULL)
    INSERT INTO @Test VALUES (2, 5, 1, '01-12-2012', 'GSM', NULL)
    INSERT INTO @Test VALUES (3, 1, 1, '01-01-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (3, 2, 0, '01-02-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (4, 1, 1, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (4, 2, 0, '01-02-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (4, 3, 0, '01-03-2012', 'FSA', NULL)
    INSERT INTO @Test VALUES (5, 1, 0, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (5, 2, 1, '01-02-2012', 'LSI', NULL)
    INSERT INTO @Test VALUES (5, 3, 0, '01-03-2012', 'LSI', NULL)
    INSERT INTO @Test VALUES (6, 1, 1, '01-01-2012', NULL, NULL)
    INSERT INTO @Test VALUES (6, 2, 0, '01-02-2012', 'LSI', NULL)
    INSERT INTO @Test VALUES (6, 3, 1, '01-03-2012', 'LSI', NULL)

    SELECT * FROM @Test AS FakedOutput_2

尝试的解决方案
这是我到目前为止提出的,但它失败了我的第一个测试用例(GID=1

;WITH CTE AS ( 
    -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    SELECT  T.GID, T.SEQ, T.IsLive, NULL cGuid, NULL cSEQ, 
            cast(0 as bit) cIsLive, T.Name, T.Salary 
    FROM    @Test T
    JOIN    @Test S ON T.GID = S.GID AND T.Seq = S.Seq AND S.IsLive = 0
    -- - - - - - - 
      UNION ALL 
    -- - - - - - - 
    SELECT  t.GID, t.SEQ, T.IsLive, c.GID cGID, c.Seq cSEQ, 
             c.IsLive cIsLive, ISNULL(C.Name, T.Name), 
             ISNULL(t.Salary, c.Salary) 
    FROM    CTE c 
    JOIN    @Test t ON    t.GID     = c.GID   AND 
                          t.Seq     > c.Seq   AND 
                          t.IsLive  = 1       AND 
                          c.IsLive  = 0
    -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
) 
--SELECT * FROM CTE ORDER BY CTE.GID, CTE.Seq
UPDATE  t 
SET     Name = c.Name, Salary = c.Salary
FROM    @Test t 
JOIN    CTE c     ON c.GID = t.GID AND c.Seq = t.SEQ
WHERE   C.cIsLive IS NOT NULL
4

2 回答 2

1

使用APPLY适用于您的测试用例。以下给出与您的解决方案2相同的内容

SELECT  t1.GID,
        t1.Seq,
        t1.IsLive,
        t1.Eff,
        CASE WHEN t1.IsLive = 0 THEN COALESCE(t1.Name, t3.Name) ELSE COALESCE(t3.Name, t1.Name) END AS Name,
        Salary
FROM    @Test T1
        OUTER APPLY
        (   SELECT  TOP 1 Name
            FROM    @Test T2
            WHERE   T2.GID = T1.GID
            AND     T2.Seq < T1.Seq
            AND     t2.IsLive = 0
            AND     t2.Name IS NOT NULL
            ORDER BY Seq DESC
        ) t3

编辑

刚刚注意到需要一个UPDATE

UPDATE  @Test
SET     Name = CASE WHEN t1.IsLive = 0 THEN COALESCE(t1.Name, t3.Name) ELSE COALESCE(t3.Name, t1.Name) END
FROM    @Test T1
        OUTER APPLY
        (   SELECT  TOP 1 Name
            FROM    @Test T2
            WHERE   T2.GID = T1.GID
            AND     T2.Seq < T1.Seq
            AND     t2.IsLive = 0
            AND     t2.Name IS NOT NULL
            ORDER BY Seq DESC
        ) t3

编辑 2

我稍微改变了apply中的查询,现在它将尝试找到live = 0且名称不为null的最近行,如果没有live = 1的行(如GID = 4),它将采用名称不为空的最近行:

UPDATE  @Test
SET     Name = CASE WHEN t1.IsLive = 0 THEN COALESCE(t1.Name, t3.Name) ELSE COALESCE(t3.Name, t1.Name) END
FROM    @Test T1
        OUTER APPLY
        (   SELECT  TOP 1 Name
            FROM    @Test T2
            WHERE   T2.GID = T1.GID
            AND     T2.Seq < T1.Seq
            AND     t2.Name IS NOT NULL
            ORDER BY t2.IsLive, Seq DESC
        ) t3
于 2012-09-25T15:10:00.603 回答
1

我花了一段时间才意识到这个问题只是我之前发布的问题的一个微小变化。刚离开键盘一段时间就帮我看到了答案!@GarethDs 的回答也为此做出了贡献。

;WITH CTE AS ( 
    SELECT  T.GID, T.SEQ, T.IsLive, Name, Salary 
    FROM    @Test T
    JOIN    ( SELECT   GID, MIN(Seq) Seq 
              FROM     @Test 
              GROUP BY GID     
            ) S ON T.GID = S.GID AND T.Seq = S.Seq

    UNION ALL 

    SELECT t.GID, t.SEQ, T.IsLive,
           CASE WHEN T.IsLive = 0 THEN COALESCE(T.Name, C.Name) 
                ELSE COALESCE(C.Name, T.Name) END,
           CASE WHEN T.IsLive = 0 THEN COALESCE(T.Salary, C.Salary) 
                ELSE COALESCE(C.Salary, T.Salary) END 
    FROM   CTE C
    JOIN   @Test T ON T.GID = C.GID AND T.SEQ = C.SEQ+1 
) 
--SELECT * FROM CTE ORDER BY CTE.GID, CTE.Seq
UPDATE T 
SET    Name   = C.Name,  
       Salary =  C.Salary
FROM   @Test T
JOIN   CTE C ON C.GID = T.GID AND C.Seq = T.SEQ
于 2012-09-26T10:20:09.587 回答