检查下一个周期是否从上一个周期的末尾开始

时间:2020-08-21 03:31:58

标签: sql-server date tsql

我有几种用法。一种是准备另一种是实际用法。

需要检查上一时期的预付费类型开始和结束日期是否等于下一个时期的(相等)开始和结束日期

期末,我已经准备好使用量,根据逻辑上,下期的使用量必须与上期的预付款匹配。但是有时候我会遇到这两种类型不匹配的情况。 例如

  1  (128193    ,'2020-05-01',  '2020-05-31','prepaid'),
  2  (134145    ,'2020-06-01',  '2020-06-30','prepaid'),
  3  (134145    ,'2020-05-01',  '2020-05-30','usage'),

示例记录编号3应与该期间的预付款记录编号1匹配,在这种情况下,它们使用在时间段134145中的开始和结束日期不等于预付款在上期128193中。

我需要找到那些情况。

我创建了一个脚本

DECLARE @t TABLE ( id INT, startdate DATE, enddate DATE , u_type VARCHAR(255))

INSERT  INTO @t
VALUES  ( 128193    ,'2020-05-01',  '2020-05-31','prepaid'),
         (134145    ,'2020-06-01',  '2020-06-30','prepaid'),
         (134145    ,'2020-05-01',  '2020-05-30','usage'),
         (141461    ,'2020-07-01',  '2020-07-31','prepaid'),
         (141461    ,'2020-06-01',  '2020-06-30','usage');
WITH    cte
          AS ( SELECT   ROW_NUMBER() OVER ( ORDER BY id ) AS r ,
                        id ,
                        startdate ,
                        enddate,
                        u_type
               FROM     @t
             )
    SELECT  c2.id ,
            c1.id, 
            c1.startdate,
            c1.enddate,
            c2.startdate,
            c2.enddate,
            c1.u_type,
            c2.u_type
    FROM    cte c1
            JOIN cte c2 ON c2.r + 1 = c1.r AND  c2.u_type <> c1.u_type
    WHERE   DATEADD(d, 1, c2.enddate) <> c1.startdate

但是它会返回错误的结果。

这是一次运行,所以我不在乎性能速度是否可以使用#tables CTE所需的任何内容,以及有关如何解决它的任何想法?

预付款记录始终会在未来期间使用。

2 个答案:

答案 0 :(得分:2)

您可以像下面这样使用CTELEAD

DECLARE @t TABLE ( id INT, startdate DATE, enddate DATE , u_type VARCHAR(255))

INSERT  INTO @t
VALUES  ( 128193    ,'2020-05-01',  '2020-05-31','prepaid'),
         (134145    ,'2020-06-01',  '2020-06-30','prepaid'),
         (134145    ,'2020-05-01',  '2020-05-30','usage'),
         (141461    ,'2020-07-01',  '2020-07-31','prepaid'),
         (141461    ,'2020-06-01',  '2020-06-30','usage');

WITH CTE_PERIOD_RANGE
     AS (SELECT ID, 
                STARTDATE, 
                ENDDATE, 
                U_TYPE, 
                LEAD(STARTDATE, 1) OVER(PARTITION BY U_TYPE ORDER BY STARTDATE) AS NEXT_PERIOD_STARTDATE, 
                LEAD(ENDDATE, 1) OVER(PARTITION BY U_TYPE ORDER BY STARTDATE) AS NEXT_PERIOD_ENDDATE
         FROM @t)
     SELECT ID, 
            STARTDATE, 
            ENDDATE, 
            U_TYPE
     FROM CTE_PERIOD_RANGE
     WHERE STARTDATE <> NEXT_PERIOD_STARTDATE OR 
           ENDDATE <> NEXT_PERIOD_ENDDATE AND 
           NEXT_PERIOD_STARTDATE IS NOT NULL; -- this filters out the last occurrence of each type, because this record has no following period

这将返回:

╔════════╦════════════╦════════════╦═════════╗
║   id   ║ startdate  ║  enddate   ║ u_type  ║
╠════════╬════════════╬════════════╬═════════╣
║ 128193 ║ 2020-05-01 ║ 2020-05-31 ║ prepaid ║
║ 134145 ║ 2020-06-01 ║ 2020-06-30 ║ prepaid ║
║ 134145 ║ 2020-05-01 ║ 2020-05-30 ║ usage   ║
╚════════╩════════════╩════════════╩═════════╝

请参见SQLFiddle:demo

答案 1 :(得分:1)

不是吗

select
  p.id        as p_id,        u.id        as u_id,
  p.startdate as p_startdate, u.startdate as u_startdate,
  p.enddate   as p_enddate,   u.enddate   as u_enddate
from
  @t u
    inner join @t p
    on(
          p.u_type = 'prepaid'
      --
      and p.startdate <= u.enddate
      and p.enddate   >= u.startdate
    )          
where
    u.u_type = 'usage'
--
and (
     p.startdate <> u.startdate
  or p.enddate   <> u.enddate
)

我们寻找与prepaid相匹配的usage,然后检查其边界。