2017-04-03 70 views
4

我有一份工作清单,显示任何特定工作所进行的工作。在白天完成工作时,只添加一条记录并包含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 
+0

这个问题是否相似? [14523906](http://stackoverflow.com/questions/14523906/find-users-who-worked-for-5-consecutive-days-with-date-range-in-output?rq=1) – Todd

+0

@Todd - 谢谢。它确实看起来很相似,但我肯定需要在这个范围内留有空隙以便周末。 – BriteSponge

回答

2

您可以确定它使用一周中的一天(星期一= 0,星期日= 6):

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

,并使用LAG解析函数,那么你可以比较是否上一个条目是以前的工作日,并以此来确定组:

甲骨文设置

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()了alytic功能,并指定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 
+0

第一个查询似乎计算作业ID列出的天数,无论它们是否连续。但是,第二个查询似乎给了我想要的。在我将其标记为“已接受”之前,我会尝试更多的情况。我真的需要在分析查询方面做更多的工作。 – BriteSponge

+0

@BriteSponge如果存在间隙,第一个查询不会将他们不连续工作日的时间分组,如果存在间隙,它会给出组的范围。第二个将告诉你哪些工作从来没有连续10个工作日的时间,但不能告诉你什么时候工作。 – MT0

+0

第二个可能就足够了。请求这个人不是非常具体。 – BriteSponge

0

10天= 2整周。对于天,你可以看看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追溯到是在过去的两周内,则有两个日期的整整两周。

+0

你是一个源源不断的知识... –

+0

你确定你打算使用'ROWS BETWEEN'吗?您可以有13个前面的行跨越几个月的范围。 – MT0

+0

我试过这个,但似乎无法得到结果与我的测试数据。把它分解一点我认为(不确定)它可能与窗口子句有关。顺便说一下,HAVING子句的尾括号是错误的。感谢你的回答。 – BriteSponge

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 
+0

'TO_CHAR(DATE'2017-04-03','D')'(星期一)给出'1',所以这似乎忽略了周日和周一。您需要指定适用的'NLS_TERRITORY',或者使用您的数据库的NLS设置不相关的东西。 – MT0

+0

你是对的,我编辑了我的答案 –

+0

谢谢你的回答斯特凡诺。这可能是我的数据,但我似乎无法得到此方法为我返回结果。 – BriteSponge

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; 
+0

我喜欢查询可以很容易地分解显示逻辑,但不幸的是我没有日历表(这就是为什么我可以忽略公共假期)。 – BriteSponge