查找跨越周末的连续日期

时间:2017-04-03 13:18:14

标签: sql oracle

我有一份工作清单,表明在任何特定工作上执行的工作。当白天完成工作时,只添加一条记录并包含work_type。 周末不进行工作。乔布斯可以在很长一段时间内完成工作,在这里和那里度过了奇怪的一天,但在其生命周期的某个阶段,它应该有一段时间的工作,以便始终如一地进行工作。 我们的管理层希望能够在报告中突出显示没有发生这种较长时间工作的任何工作。 围绕工作类型和团队名称还有其他一些条件,但主要的问题是时间问题。

那么......我怎样才能找到工作时间连续不超过两周(10个工作日)的工作?

在下文中,作业164353将不包括在内,因为它必须连续10天(忽略周末),而作业214325将被标记为9日有间隔打破连续几天的序列。

JOB_ID     W ACTION_DATE
---------- - -----------
164354     H 10-FEB-17
164354     H 13-FEB-17
164354     H 14-FEB-17
164354     H 15-FEB-17
164354     H 16-FEB-17
164354     H 17-FEB-17
164354     H 20-FEB-17
164354     H 21-FEB-17
164354     H 22-FEB-17
164354     H 23-FEB-17
164354     H 24-FEB-17

214325     H 01-MAR-17
214325     H 02-MAR-17
214325     H 03-MAR-17
214325     H 06-MAR-17
214325     H 07-MAR-17
214325     H 08-MAR-17
214325     H 10-MAR-17
214325     H 13-MAR-17
214325     H 14-MAR-17
214325     H 15-MAR-17

我有这个查询,我可以针对每个小组制作连续几天的小组,但是我很难适应周末的情况。换句话说,下面的结果理想情况下会显示连续几天的10天。

WITH  
  groups AS (
    SELECT
      ROW_NUMBER() OVER (ORDER BY action_date) AS rn,
      action_date -ROW_NUMBER() OVER (ORDER BY action_date) AS grp,
      action_date
    FROM test_job_list
    WHERE job_id = 164354
  )
SELECT count(*) AS num_consec_dates,
       min(action_date) AS earliest,
       max(action_date) AS latest
FROM groups
group by grp
ORDER BY num_consec_dates desc, earliest desc


NUM_CONSEC
DATES      EARLIEST  LATEST
---------- --------- ---------
         5 20-FEB-17 24-FEB-17
         5 13-FEB-17 17-FEB-17
         1 10-FEB-17 10-FEB-17

4 个答案:

答案 0 :(得分:2)

您可以确定它正在使用的星期几(星期一= 0,星期日= 6):

TRUNC( action_date ) - TRUNC( action_date, 'IW' )

然后,使用LAG分析函数,您可以比较前一个条目是否是前一个工作日,并使用它来确定该组:

Oracle安装程序

CREATE TABLE test_job_list ( JOB_ID,     W, ACTION_DATE ) AS
SELECT 164354, 'H', DATE '2017-02-10' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-13' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-14' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-15' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-16' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-17' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-20' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-21' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-22' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-23' FROM DUAL UNION ALL
SELECT 164354, 'H', DATE '2017-02-24' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-01' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-02' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-03' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-06' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-07' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-08' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-10' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-13' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-14' FROM DUAL UNION ALL
SELECT 214325, 'H', DATE '2017-03-15' FROM DUAL;

<强>查询

SELECT job_id,
       MIN( action_date ) AS start_date,
       MAX( action_date ) AS end_date,
       COUNT( 1 ) AS num_days
FROM   (
  SELECT job_id,
         action_date,
         SUM( has_changed_group ) OVER ( PARTITION BY job_id ORDER BY action_date )
           AS group_id
  FROM   (
    SELECT job_id,
           action_date,
           CASE WHEN
             LAG( action_date ) OVER ( PARTITION BY job_id ORDER BY action_date )
             = action_date - CASE TRUNC( action_date ) - TRUNC( action_date, 'IW' )
                             WHEN 0 THEN 3 ELSE 1 END
             THEN 0
             ELSE 1
           END AS has_changed_group
    FROM   test_job_list
  )
)
GROUP BY job_id, group_id
-- HAVING COUNT(1) >= 10;

<强>输出

    JOB_ID START_DATE          END_DATE              NUM_DAYS
---------- ------------------- ------------------- ----------
    164354 2017-02-10 00:00:00 2017-02-24 00:00:00         11
    214325 2017-03-10 00:00:00 2017-03-15 00:00:00          4
    214325 2017-03-01 00:00:00 2017-03-08 00:00:00          6

<强>替代

如果您只想要从未连续10个工作日的工作,那么您可以使用COUNT()分析函数并指定RANGE窗口:

SELECT job_id
FROM   (
  SELECT job_id,
         COUNT( 1 ) OVER ( PARTITION BY job_id
                           ORDER BY action_date
                           RANGE BETWEEN INTERVAL '13' DAY PRECEDING
                                 AND     INTERVAL  '0' DAY FOLLOWING )
           AS num_days
  FROM   test_job_list
)
GROUP BY job_id
HAVING MAX( num_days ) < 10;

<强>输出

    JOB_ID
----------
    214325

答案 1 :(得分:1)

修改2

第一个版本有很多问题,这个问题应该有效。

一个选项是在job_id上将表连接到自身,并在右侧仅过滤左侧日期之前两周的行。然后你可以计算重新定位日期。

select  JOB_ID
from    (
            select  g1.JOB_ID, count(g2.ACTION_DATE) CNT
            from    GROUPS g1
            join    GROUPS g2
            on      g1.JOB_ID = g2.JOB_ID
            where   g2.ACTION_DATE between g1.ACTION_DATE - 13 and g1.ACTION_DATE
            group by g1.JOB_ID, g1.ACTION_DATE
        ) t1
group by JOB_ID
having   max(CNT) < 10

答案 2 :(得分:0)

10天=整整2周。对于 11 天,您可以查看14天前的日期,看看它是否恰好是两周前:

select tjl.*,
       lag(action_date, 10) over (partition by job id order by action_date) as minad_2weeks
from test_job_list;

一个简单的技巧可以工作10天:

然后,您可以使用聚合来获得没有这段时间的工作:

select job_id
from (select tjl.*,
             lag(action_date, 9) over (partition by job_id order by action_date) as lag9_ad
      from test_job_list tjl
     ) tjl
group by job_id
having max(action_date - lag9_ad) > action_date - 14;

也就是说,如果第9个日期回溯在过去两周内,则有两个完整的日期。

答案 3 :(得分:0)

我知道这个解决方案太长了,但你可以通过逐步执行来查看有关查询的所有细节

create table calendar1 as
select day_id,WEEK_DAY_SHORT,day_num_of_week  from  VITDWH.DW_MIS_TAKVIM as calendar order by day_id;

CREATE TABLE JOB_LIST (JOB_ID NUMBER,ACTION_DATE DATE);

INSERT INTO JOB_LIST VALUES(164354,TO_DATE('10-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('13-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('14-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('15-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('16-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('17-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('20-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('21-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('22-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('23-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(164354,TO_DATE('24-FEB-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('01-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('02-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('03-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('06-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('07-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('08-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('10-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('13-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('14-MAR-17','DD-MON-YY'));
INSERT INTO JOB_LIST VALUES(214325,TO_DATE('15-MAR-17','DD-MON-YY'));

COMMIT;
with a1 as 
(
select A.JOB_ID,A.ACTION_DATE,B.DAY_ID,
       (case when action_date is not null and lag(action_date) over(partition by job_id order by day_id) is null then action_date else null end) start_date,
       (case when action_date is not null and lead(action_date) over(partition by job_id order by day_id) is  null then action_date else null end) max_date 
  from
(
select * from  calendar1 
 WHERE DAY_ID >=(select MIN(ACTION_DATE) from  JOB_LIST) 
   AND DAY_ID <= (select MAX(ACTION_DATE) from  JOB_LIST)
ORDER BY DAY_ID   
)
B  LEFT OUTER JOIN
 JOB_LIST A 
 PARTITION BY (A.JOB_ID) ON (A.ACTION_DATE= B.DAY_ID) 
ORDER BY A.JOB_ID,DAY_ID
)
,a2 as 
(
select * from  a1 where start_date is not null or max_date is not null
)
,a3 as 
(
select a2.*,lead(max_date) over(partition by job_id order by day_id)  end_date
 from  a2
)
select a.job_id,a.start_date,nvl(a.maX_date,a.end_date) end_date, (nvl(a.maX_date,a.end_date) -a.start_date) +1 date_count 
 from a3 a where start_date is not null;