微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

月底的休息时间

如何解决月底的休息时间

sql Server 2017

CREATE TABLE [TABLE_1] 
(
    PLAN_NR decimal(28,6) NULL,START_DATE datetime NULL,);

INSERT INTO TABLE_1 (PLAN_NR,START_DATE) 
VALUES (1,'2020-05-01'),(2,'2020-08-01');

CREATE TABLE [TABLE_2] 
(
    PLAN_NR decimal(28,PERIOD_NR decimal(28,6) NOT NULL
);
   
INSERT INTO TABLE_2 (PLAN_NR,PERIOD_NR) 
VALUES (1,1),(1,2),3),4),5),6),7),8),8);

SQL-FIDDLE-LINK

TABLE_1 中有计划编号和计划开始日期。

TABLE_2 包含每个计划编号的期间编号。

我想计算相应的期间开始日期:

每个期间正好是 7 天,除非期间包含月末。那么这个期间应该被划分为月末之前到月底最后一天的区间和月末之后的区间。

选择:

SELECT 
    t1.PLAN_NR,t2.PERIOD_NR,FORMAT(DATEADD (d,((t2.PERIOD_NR-1)*7),t1.START_DATE ),'yyyy-MM-dd') START_DATE
FROM
    TABLE_1 t1
JOIN
    TABLE_2 t2 ON t1.PLAN_NR = t2.PLAN_NR
ORDER BY 
    t1.PLAN_NR,t2.PERIOD_NR ASC

这将返回开始数据,但没有额外考虑相应的月末:

+---------+-----------+------------+
| PLAN_NR | PERIOD_NR | START_DATE |
+---------+-----------+------------+
|       1 |         1 | 2020-05-01 |
|       1 |         2 | 2020-05-08 |
|       1 |         3 | 2020-05-15 |
|       1 |         4 | 2020-05-22 |
|       1 |         5 | 2020-05-29 |
|       1 |         6 | 2020-06-05 |
|       1 |         7 | 2020-06-12 |
|       1 |         8 | 2020-06-19 |
|       2 |         1 | 2020-08-05 |
|       2 |         2 | 2020-08-12 |
|       2 |         3 | 2020-08-19 |
|       2 |         4 | 2020-08-26 |
|       2 |         5 | 2020-09-01 |
|       2 |         6 | 2020-09-02 |
|       2 |         7 | 2020-09-09 |
|       2 |         8 | 2020-09-16 |
+---------+-----------+------------+

我想要这样的输出

+---------+-----------+----------------------+
| PLAN_NR | PERIOD_NR |      START_DATE      |
+---------+-----------+----------------------+
|       1 |         1 | 2020-05-01           |
|       1 |         2 | 2020-05-08           |
|       1 |         3 | 2020-05-15           |
|       1 |         4 | 2020-05-22           |
|       1 |         5 | 2020-05-29           |< --- period part before new month
|       1 |         6 | 2020-06-01           |< --- period part after new month
|       1 |         7 | 2020-06-05           |
|       1 |         8 | 2020-06-12           |
|       2 |         1 | 2020-08-05           |
|       2 |         2 | 2020-08-12           |
|       2 |         3 | 2020-08-19           |
|       2 |         4 | 2020-08-26           |< --- period part before new month
|       2 |         5 | 2020-09-01           |< --- period part after new month
|       2 |         6 | 2020-09-02           |
|       2 |         7 | 2020-09-09           |
|       2 |         8 | 2020-09-16           |
+---------+-----------+----------------------+

解决方法

使用窗口函数(LEAD / LAG)获取周期的开始和结束...

    SELECT t1.PLAN_NR,t2.PERIOD_NR,FORMAT(DATEADD (d,((t2.PERIOD_NR-1)*7),t1.START_DATE ),'yyyy-MM-dd') START_DATE,CASE
       WHEN 
        lead(
          FORMAT(DATEADD (d,'yyyy-MM-dd')
          ) over (partition by 
                    FORMAT(DATEADD (d,'yyyy-MM')
                  order by t2.period_nr)
         IS NULL THEN '< --- period part before new month'
       WHEN lag(
          FORMAT(DATEADD (d,'yyyy-MM')
                  order by t2.period_nr)
         IS NULL THEN '< --- period part after new month'
       END as period_break
    from TABLE_1 t1
    join TABLE_2 t2
    on t1.PLAN_NR = t2.PLAN_NR
    order by t1.PLAN_NR,t2.PERIOD_NR asc

SQL Fiddle

PLAN_NR PERIOD_NR   START_DATE  period_break
1       1           2020-05-01  < --- period part after new month
1       2           2020-05-08  (null)
1       3           2020-05-15  (null)
1       4           2020-05-22  (null)
1       5           2020-05-29  < --- period part before new month
1       6           2020-06-05  < --- period part after new month
1       7           2020-06-12  (null)
1       8           2020-06-19  < --- period part before new month
2       1           2020-08-01  < --- period part after new month
2       2           2020-08-08  (null)
2       3           2020-08-15  (null)
2       4           2020-08-22  (null)
2       5           2020-08-29  < --- period part before new month
2       6           2020-09-05  < --- period part after new month
2       7           2020-09-12  (null)
2       8           2020-09-19  < --- period part before new month
,
SELECT 
    t1.PLAN_NR,--row_number() over() but what if PERIOD_NR is not consecutive?
    t2.PERIOD_NR + SUM(num.n) OVER(PARTITION BY t2.PLAN_NR ORDER BY t2.PERIOD_NR,num.n) AS PERIOD_NR_x,FORMAT(CASE WHEN num.n = 1 THEN DATEADD(day,1,EOMONTH(DATEADD (d,t1.START_DATE ))) ELSE DATEADD(d,t1.START_DATE ) END,'yyyy-MM-dd') START_DATE
FROM
    TABLE_1 t1
JOIN
    TABLE_2 t2 ON t1.PLAN_NR = t2.PLAN_NR
CROSS APPLY
(
    SELECT 0 AS n
    UNION ALL
    --new row for month change
    SELECT 1 AS n
    WHERE DATEDIFF(month,DATEADD(d,(t2.PERIOD_NR-1)*7,t1.START_DATE),t2.PERIOD_NR*7,t1.START_DATE)) = 1
) as num    
ORDER BY 
    t1.PLAN_NR,t2.PERIOD_NR ASC

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。