日期逻辑之间的间隔检测在第一个分组记录上失败

时间:2019-05-03 12:56:35

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

在最近询问之前,我已经成功建立了一个查询,该查询可以计算每个用户日志的间隔。 CTE使用ROW_NUMBER()按用户ID列出每个用户日志,然后在第二个CTE中,它自动加入“ RN ID + 1”,从而对“ INIx日期-FINx-1”进行“递归”计算,稍后我添加视觉帮助指示器。按原样运行查询,它几乎可以正确返回几乎每一行,但是我注意到在某些情况下,第一个顺序日志被列为“ NOSEQ”(无顺序),因为我的指示器仅将“ 1”作为连续条件。

CTE RESULT

其中RN = 5时是连续的,但由于它使用以下日志进行计算,因此不符合要求。我不知道我是否缺少某些东西,或者我必须重新开始...感谢查询逻辑方面的任何帮助。 (考虑到我仅限于SQL Server 2008 sintax)

IF NOT EXISTS (
    select * from sysobjects where name='INF_LIC' and xtype='U'
) CREATE TABLE INF_LIC (
    [PER_PRO_ID_HR_INI_FIN] NVARCHAR(57)
);
INSERT INTO INF_LIC VALUES
    (N'201811;P1;2018-11-23 00:00:00.000;2018-11-23 00:00:00.000'),
    (N'201810;P1;2018-10-25 00:00:00.000;2018-10-26 00:00:00.000'),
    (N'201809;P1;2018-09-28 00:00:00.000;2018-09-28 00:00:00.000'),
    (N'201808;P1;2018-08-31 00:00:00.000;2018-09-05 00:00:00.000'),
    (N'201807;P1;2018-07-05 00:00:00.000;2018-07-25 00:00:00.000'),
    (N'201806;P1;2018-06-14 00:00:00.000;2018-07-04 00:00:00.000'),
    (N'201805;P1;2018-05-25 00:00:00.000;2018-06-13 00:00:00.000'),
    (N'201805;P1;2018-05-10 00:00:00.000;2018-05-24 00:00:00.000'),
    (N'201804;P1;2018-04-25 00:00:00.000;2018-05-09 00:00:00.000');

;WITH CTE AS(
    -- LIST EVERY RECORD
    SELECT ID, CAST(INI AS DATETIME) AS INI, CAST(FIN AS DATETIME) AS FIN, 
        RN = ROW_NUMBER()OVER(PARTITION BY ID ORDER BY CAST(FIN AS DATETIME) DESC)
    FROM INF_LIC
    WHERE PER_PRO > 201712
), CTE2 AS(
    -- CALCULATE DATEDIFF
    SELECT T.*, 
        -- DATEDIFF BETWEEN INIn - FINn-1
        DD = CASE WHEN DATEDIFF(DD,T.FIN,T2.INI) IS NULL THEN 0 ELSE DATEDIFF(DD,T2.INI,T.FIN) END
    FROM CTE 
    -- LEFT JOIN ON EQUAL ID's AND RN = RN+1
    LEFT JOIN CTE T2 ON T.RN = T2.RN + 1 AND T.ID = T2.ID AND T.RN <> T2.RN
), CTE3 AS(
    SELECT ID, INI, FIN, RN, ABS(DD) AS DD,
    -- INDICATOR, IF -1 ITS 'SEQ', NULL MARKS THE NEWEST LOG 'FIRSTLOG' ELSE IT'S NOT SEQUENTIAL
    IND = (CASE WHEN DD = -1 THEN 'SEQ'
        WHEN DD = 0 THEN 'FIRSTLOG'
        ELSE 'NOSEQ'
        END)
    FROM CTE2
), CTE4 AS(
    SELECT ID, INI, FIN, RN, DD, IND
    FROM CTE3
    GROUP BY ID, INI, FIN, RN, DD, IND 
)
SELECT * FROM CTE4
ORDER BY ID, RN ASC

1 个答案:

答案 0 :(得分:0)

尚不清楚您为什么相信记录5是连续的。问题中的SQL暗示顺序的定义是上一条记录的ini与该记录的fin相隔1天,这是事实。

但是,鉴于这些列名,这是一个可疑的定义。我怀疑您想定义ind以便当该记录的fin与下一条记录的ini相隔1天时是连续的。如果是这样,那么您可能想要这样做:

WITH CTE AS(
    -- LIST EVERY RECORD
    SELECT ID, CAST(INI AS DATETIME) AS INI, CAST(FIN AS DATETIME) AS FIN, 
        RN = ROW_NUMBER()OVER(PARTITION BY ID ORDER BY CAST(FIN AS DATETIME) DESC)
    FROM INF_LIC
    WHERE PER_PRO > 201712
), CTE2 AS(
    -- CALCULATE DATEDIFF
    SELECT T.*, 
        -- DATEDIFF BETWEEN INIn - FINn-1
        DD = CASE WHEN DATEDIFF(DD,T2.FIN,T.INI) IS NULL THEN 0 ELSE DATEDIFF(DD,T.INI,T2.FIN) END
    FROM CTE T
    -- LEFT JOIN ON EQUAL ID's AND RN = RN+1
    LEFT JOIN CTE T2 ON T.RN = T2.RN - 1 AND T.ID = T2.ID
), CTE3 AS(
    SELECT ID, INI, FIN, RN, ABS(DD) AS DD,
    -- INDICATOR, IF -1 ITS 'SEQ', NULL MARKS THE NEWEST LOG 'FIRSTLOG' ELSE IT'S NOT SEQUENTIAL
    IND = (CASE WHEN DD in (0, -1) THEN 'SEQ'
        WHEN RN = 1 THEN 'FIRSTLOG'
        ELSE 'NOSEQ'
        END)
    FROM CTE2
), CTE4 AS(
    SELECT ID, INI, FIN, RN, DD, IND
    FROM CTE3
    GROUP BY ID, INI, FIN, RN, DD, IND 
)
SELECT * FROM CTE4
ORDER BY ID, RN ASC

相关更改为:

LEFT JOIN CTE T2 ON T.RN = T2.RN - 1 AND T.ID = T2.ID

该联接现在为T2.RN减1:您需要减去下一条记录的行号才能获取,而不是添加,并且:

IND = (CASE WHEN DD in (0, -1) THEN 'SEQ'
        WHEN RN = 1 THEN 'FIRSTLOG'
        ELSE 'NOSEQ'
        END)

由于您已经在进行行编号,因此最好使用明显的FIRSTLOG定义。由于此连接顺序现在从原始假设开始倒退,因此DD为0意味着它是最后一个记录,而不是第一个记录,因此它会稍微扩展SEQ的定义。