计算总天数,包括前一个时段,取决于SQL中的条件

时间:2018-04-16 12:04:10

标签: sql oracle

我需要计算各种员工的病假期限。如果两个病假之间的间隔时间少于28天,那么它们被视为一个时期,因此病假的总持续时间是病假的当前天数+之前的病假总天数。我最后得到了下表:

Key-    Start date- End date-   Duration in days-   Duration of last sickleave- Less the 28 days-
0001    01-01-2015  14-01-2015  13  0   Yes
0001    03-03-2015  19-03-2015  16  13  No
0001    27-05-2015  28-05-2015  1   16  No
0001    18-08-2015  31-08-2015  13  1   No
0001    24-09-2015  05-10-2015  11  13  Yes
0001    21-10-2015  29-10-2015  8   11  Yes
0001    05-11-2015  09-11-2015  4   8   Yes
0001    07-12-2015  08-12-2015  1   4   No
0001    21-12-2015  28-12-2015  7   1   Yes
0001    12-01-2016  18-01-2016  6   7   Yes
0001    08-02-2016  29-02-2016  21  6   Yes

我想最终得到这张表:

Key-    Start date- End date-   Duration in days-   Duration of last sickleave- Less the 28 days-   Total number of days-
0001    01-01-2015  14-01-2015  13  0   Yes 13
0001    03-03-2015  19-03-2015  16  13  No  16
0001    27-05-2015  28-05-2015  1   16  No  1
0001    18-08-2015  31-08-2015  13  1   No  13
0001    24-09-2015  05-10-2015  11  13  Yes 24
0001    21-10-2015  29-10-2015  8   11  Yes 32
0001    05-11-2015  09-11-2015  4   8   Yes 36
0001    07-12-2015  08-12-2015  1   4   No  1
0001    21-12-2015  28-12-2015  7   1   Yes 8
0001    12-01-2016  18-01-2016  6   7   Yes 14
0001    08-02-2016  29-02-2016  21  6   Yes 35

如何在SQL中实现这一点(使用Oracle数据库)?因此,当'少于28天' ='是'和'少于28天'在上一行中是'是',然后'总天数' ='总天数' +'最后一次镰刀的持续时间'否则'总天数' ='持续时间以天为单位'

1 个答案:

答案 0 :(得分:2)

不是试图将之前的疾病日添加到当前的疾病持续时间,为什么不只是做一个滚动的总和呢?

WITH           your_table AS (SELECT '0001' key_, to_date('01-01-2015', 'dd-mm-yyyy') start_date, to_date('14-01-2015', 'dd-mm-yyyy') end_date, 13 duration_in_days, 0 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('03-03-2015', 'dd-mm-yyyy') start_date, to_date('19-03-2015', 'dd-mm-yyyy') end_date, 16 duration_in_days, 13 duration_of_last_sickleave, 'No' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('27-05-2015', 'dd-mm-yyyy') start_date, to_date('28-05-2015', 'dd-mm-yyyy') end_date, 1 duration_in_days, 16 duration_of_last_sickleave, 'No' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('18-08-2015', 'dd-mm-yyyy') start_date, to_date('31-08-2015', 'dd-mm-yyyy') end_date, 13 duration_in_days, 1 duration_of_last_sickleave, 'No' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('24-09-2015', 'dd-mm-yyyy') start_date, to_date('05-10-2015', 'dd-mm-yyyy') end_date, 11 duration_in_days, 13 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('24-10-2015', 'dd-mm-yyyy') start_date, to_date('29-10-2015', 'dd-mm-yyyy') end_date, 8 duration_in_days, 11 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('05-11-2015', 'dd-mm-yyyy') start_date, to_date('09-11-2015', 'dd-mm-yyyy') end_date, 4 duration_in_days, 8 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('07-12-2015', 'dd-mm-yyyy') start_date, to_date('08-12-2015', 'dd-mm-yyyy') end_date, 1 duration_in_days, 4 duration_of_last_sickleave, 'No' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('21-12-2015', 'dd-mm-yyyy') start_date, to_date('28-12-2015', 'dd-mm-yyyy') end_date, 7 duration_in_days, 1 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('12-01-2016', 'dd-mm-yyyy') start_date, to_date('18-01-2016', 'dd-mm-yyyy') end_date, 6 duration_in_days, 7 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual UNION ALL
                              SELECT '0001' key_, to_date('08-02-2016', 'dd-mm-yyyy') start_date, to_date('29-03-2016', 'dd-mm-yyyy') end_date, 21 duration_in_days, 6 duration_of_last_sickleave, 'Yes' less_than_28_days FROM dual),
  identify_sickness_start AS (SELECT key_,
                                     start_date,
                                     end_date,
                                     duration_in_days,
                                     duration_of_last_sickleave,
                                     less_than_28_days,
                                     CASE WHEN start_date - LAG(end_date, 1, start_date - 30) OVER (PARTITION BY key_ ORDER BY start_date) >= 28 THEN 1 ELSE 0 END new_sickness_period
                              FROM   your_table),
     calc_sickness_groups AS (SELECT key_,
                                     start_date,
                                     end_date,
                                     duration_in_days,
                                     duration_of_last_sickleave,
                                     less_than_28_days,
                                     sum(new_sickness_period) OVER (PARTITION BY key_ ORDER BY start_date) grp
                              FROM   identify_sickness_start)
SELECT key_,
       start_date,
       end_date,
       duration_in_days,
       duration_of_last_sickleave,
       less_than_28_days,
       sum(duration_in_days) OVER (PARTITION BY key_, grp ORDER BY start_date) total_number_of_days
FROM   calc_sickness_groups
ORDER BY key_, start_date;

KEY_ START_DATE  END_DATE    DURATION_IN_DAYS DURATION_OF_LAST_SICKLEAVE LESS_THAN_28_DAYS TOTAL_NUMBER_OF_DAYS
---- ----------- ----------- ---------------- -------------------------- ----------------- --------------------
0001 01/01/2015  14/01/2015                13                          0 Yes                                 13
0001 03/03/2015  19/03/2015                16                         13 No                                  16
0001 27/05/2015  28/05/2015                 1                         16 No                                   1
0001 18/08/2015  31/08/2015                13                          1 No                                  13
0001 24/09/2015  05/10/2015                11                         13 Yes                                 24
0001 24/10/2015  29/10/2015                 8                         11 Yes                                 32
0001 05/11/2015  09/11/2015                 4                          8 Yes                                 36
0001 07/12/2015  08/12/2015                 1                          4 No                                   1
0001 21/12/2015  28/12/2015                 7                          1 Yes                                  8
0001 12/01/2016  18/01/2016                 6                          7 Yes                                 14
0001 08/02/2016  29/03/2016                21                          6 Yes                                 35

这首先确定每个疾病组的开始(每次新的疾病期开始时,输出1;这意味着任何时候一行<=前一行后的28天,我们放1,否则a 0。

然后我们可以在此列中执行运行总和,以计算出同一个疾病组中的行。

最后,我们可以针对每组病态行在duration_in_days列中执行总计。

这意味着您不再需要计算duration_of_last_sickleave和less_than_28_days列(我假设您作为select语句的一部分而不是作为表的一部分?),因为它们不再是必需的,至少不是计算总天数!