2017-04-03 68 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