我有一個表格,其中包含每個員工的訪問時間和活動資訊。
有些員工在兩天之間有輪班作業時間,我有列確定這個訪問時間是屬于前一天還是當天。
我需要收集屬于一天的訪問時間,然后獲取當前訪問時間和下一個訪問時間之間的差異并確定運動的檢查狀態(第一次或最后一次或白天運動)。
如果員工在同一天有輪班作業時間,我會得到上述結果,但如果他在兩天之間有作業時間,我將無法得到真正的結果。
例子:
員工編號 1 有 Shift 方法:17:00 -> 01:00
員工編號 2 有輪班方法:08:30 -> 16:30
總作業時間:08:00
CREATE TABLE My_Table (
EMP_ID NUMBER(4) ,
Timeinout date ,
flag number(1)
);
INSERT INTO My_Table VALUES (1,to_date('03-07-2018 16:39:44','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (1,to_date('04-07-2018 01:14:40','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (1,to_date('04-07-2018 01:14:44 ','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (1,to_date('04-07-2018 16:14:52','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (1,to_date('05-07-2018 01:07:40','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (1,to_date('05-07-2018 01:07:44 ','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (1,to_date('05-07-2018 16:31:08','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (1,to_date('06-07-2018 01:01:48 ','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (1,to_date('06-07-2018 01:01:52','dd-mm-yyyy hh24:mi:ss'),1);
INSERT INTO My_Table VALUES (2,to_date('03-07-2018 08:37:40','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (2,to_date('03-07-2018 16:27:36','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (2,to_date('04-07-2018 08:37:04 ','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (2,to_date('04-07-2018 12:58:36','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (2,to_date('04-07-2018 13:09:48 ','dd-mm-yyyy hh24:mi:ss'),0);
INSERT INTO My_Table VALUES (2,to_date('04-07-2018 17:15:32 ','dd-mm-yyyy hh24:mi:ss'),0);
COMMIT;
我使用的查詢是針對員工編號 2 回傳真實結果:
select X.*,
CASE
WHEN Previous_Movment IS NULL AND Next_Movment IS NOT NULL THEN
'First Check'
WHEN Previous_Movment IS NOT NULL AND Next_Movment IS NULL THEN
'Last Check'
when Previous_Movment IS NULL AND Next_Movment IS NULL THEN
'Do Not have access in this day'
ELSE
'During the Day'
end CHECK_sTATUS
from (select trunc(a.timeinout) as date_,
a.timeinout current_movment,
LEAD(TIMEINOUT) OVER(partition by trunc(TIMEINOUT), Emp_ID ORDER BY TIMEINOUT, Emp_ID) Next_Movment,
LAG(TIMEINOUT) OVER(partition by trunc(TIMEINOUT), Emp_ID ORDER BY TIMEINOUT, Emp_ID) Previous_Movment,
trunc(24 *
mod(LEAD(TIMEINOUT) OVER(partition by trunc(TIMEINOUT),
Emp_ID ORDER BY TIMEINOUT,
Emp_ID) - TIMEINOUT,
1)) as diff_hours,
trunc(mod(mod(LEAD(TIMEINOUT)
OVER(partition by trunc(TIMEINOUT),
Emp_ID ORDER BY TIMEINOUT,
Emp_ID) - TIMEINOUT,
1) * 24,
1) * 60) as diff_minus,
trunc(mod(mod(mod(LEAD(TIMEINOUT)
OVER(partition by trunc(TIMEINOUT),
Emp_ID ORDER BY TIMEINOUT,
Emp_ID) - TIMEINOUT,
1) * 24,
1) * 60,
1) * 60) as diff_sec,
Emp_ID,
FLAG Return_Previous_day_or_not
from My_table a
WHERE trunc(a.timeinout) between
to_Date('03-07-2018', 'dd-mm-yyyy') and
to_Date('07-07-2018', 'dd-mm-yyyy')
and a.Emp_ID = 2
) X
并獲得員工編號 2 的以下結果:

But when I switch the query to employee number1 I get the wrong result because the movements between two days:
Note:
I used Trunc(Timeinout) in the LEAD & lAG function for the employee who has work hours on the same day.
uj5u.com熱心網友回復:
從 Oracle 12 開始,您可以使用MATCH_RECOGNIZE它。
假設您希望輪班持續不超過 12 小時(您的輪班時間似乎是 8 小時,并且人們提前入住并延遲離開),那么:
SELECT emp_id,
timeinout AS current_movement,
flag,
CASE
WHEN cls IN ('FIRST_CHECK', 'DURING_DAY')
THEN next_timeinout
END AS next_movement,
CASE
WHEN cls IN ('DURING_DAY', 'LAST_CHECK')
THEN prev_timeinout
END AS previous_movement,
CASE
WHEN cls IN ('FIRST_CHECK', 'DURING_DAY')
THEN (next_timeinout - timeinout) DAY TO SECOND
END AS diff,
cls AS check_status
FROM my_table
MATCH_RECOGNIZE(
PARTITION BY emp_id
ORDER BY timeinout
MEASURES
PREV(timeinout) AS prev_timeinout,
NEXT(timeinout) AS next_timeinout,
LAST(last_check.timeinout) AS last_timeinout,
CLASSIFIER() AS cls
ALL ROWS PER MATCH
PATTERN (first_check (during_day* last_check)?)
DEFINE
during_day AS timeinout < first_check.timeinout INTERVAL '12' HOUR,
last_check AS timeinout < first_check.timeinout INTERVAL '12' HOUR
) m
其中,對于您的樣本資料,輸出:
EMP_ID CURRENT_MOVEMENT 旗幟 NEXT_MOVEMENT PREVIOUS_MOVEMENT 差異化 檢查狀態 1 2018-07-03T16:39:44 0 2018-07-04T01:14:40 00 08:34:56.000000 FIRST_CHECK 1 2018-07-04T01:14:40 1 2018-07-04T01:14:44 2018-07-03T16:39:44 00 00:00:04.000000 DURING_DAY 1 2018-07-04T01:14:44 1 2018-07-04T01:14:40 上次檢查 1 2018-07-04T16:14:52 0 2018-07-05T01:07:40 00 08:52:48.000000 FIRST_CHECK 1 2018-07-05T01:07:40 1 2018-07-05T01:07:44 2018-07-04T16:14:52 00 00:00:04.000000 DURING_DAY 1 2018-07-05T01:07:44 1 2018-07-05T01:07:40 上次檢查 1 2018-07-05T16:31:08 0 2018-07-06T01:01:48 00 08:30:40.000000 FIRST_CHECK 1 2018-07-06T01:01:48 1 2018-07-06T01:01:52 2018-07-05T16:31:08 00 00:00:04.000000 DURING_DAY 1 2018-07-06T01:01:52 1 2018-07-06T01:01:48 上次檢查 2 2018-07-03T08:37:40 0 2018-07-03T16:27:36 00 07:49:56.000000 FIRST_CHECK 2 2018-07-03T16:27:36 0 2018-07-03T08:37:40 上次檢查 2 2018-07-04T08:37:04 0 2018-07-04T12:58:36 00 04:21:32.000000 FIRST_CHECK 2 2018-07-04T12:58:36 0 2018-07-04T13:09:48 2018-07-04T08:37:04 00 00:11:12.000000 DURING_DAY 2 2018-07-04T13:09:48 0 2018-07-04T17:15:32 2018-07-04T12:58:36 00 04:05:44.000000 DURING_DAY 2 2018-07-04T17:15:32 0 2018-07-04T13:09:48 上次檢查
db<>在這里擺弄
uj5u.com熱心網友回復:
從 Oracle 11gR2 開始,您可以使用遞回子查詢:
WITH ordered_data AS (
SELECT m.*,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY timeinout) AS rn,
LAG(timeinout) OVER (PARTITION BY emp_id ORDER BY timeinout)
AS previous_movement,
LEAD(timeinout) OVER (PARTITION BY emp_id ORDER BY timeinout)
AS next_movement
FROM my_table m
),
grouped_data (
emp_id,
current_movement,
flag,
rn,
previous_movement,
next_movement,
grp,
check_status,
first_movement
) AS (
SELECT emp_id,
timeinout,
flag,
rn,
NULL,
next_movement,
1,
'FIRST_CHECK',
timeinout
FROM ordered_data
WHERE rn = 1
UNION ALL
SELECT o.emp_id,
o.timeinout AS current_movement,
o.flag,
o.rn,
CASE
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
THEN o.previous_movement
ELSE NULL
END,
CASE
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
AND ( o.next_movement >= g.first_movement INTERVAL '12' HOUR
OR o.next_movement IS NULL)
THEN NULL
ELSE o.next_movement
END,
CASE
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
THEN g.grp
ELSE g.grp 1
END,
CASE
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
AND ( o.next_movement >= g.first_movement INTERVAL '12' HOUR
OR o.next_movement IS NULL)
THEN 'LAST_CHECK'
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
THEN 'DURING_DAY'
ELSE 'FIRST_CHECK'
END,
CASE
WHEN o.timeinout < g.first_movement INTERVAL '12' HOUR
THEN g.first_movement
ELSE o.timeinout
END
FROM ordered_data o
INNER JOIN grouped_data g
ON (o.emp_id = g.emp_id AND o.rn = g.rn 1)
)
SELECT emp_id,
current_movement,
flag,
previous_movement,
next_movement,
(next_movement - current_movement) DAY TO SECOND AS diff,
check_status
FROM grouped_data
ORDER BY emp_id, current_movement
db<>在這里擺弄
uj5u.com熱心網友回復:
您可以使用一個PIPELINED函式:
CREATE TYPE my_table_type AS OBJECT (
emp_id NUMBER,
current_movement DATE,
flag NUMBER,
previous_movement DATE,
next_movement DATE,
diff INTERVAL DAY TO SECOND,
check_status VARCHAR2(15)
);
CREATE TYPE my_table_tbltype AS TABLE OF my_table_type;
然后:
CREATE FUNCTION parse_my_table RETURN my_table_tbltype PIPELINED
AS
c_shift_length CONSTANT INTERVAL DAY TO SECOND := INTERVAL '12' HOUR;
v_emp_id MY_TABLE.EMP_ID%TYPE;
v_first_movement MY_TABLE.TIMEINOUT%TYPE;
v_previous_movement MY_TABLE.TIMEINOUT%TYPE;
v_next_movement MY_TABLE.TIMEINOUT%TYPE;
v_check_status VARCHAR2(15);
BEGIN
FOR row IN (
SELECT m.*,
LAG(timeinout) OVER (PARTITION BY emp_id ORDER BY timeinout)
AS previous_movement,
LEAD(timeinout) OVER (PARTITION BY emp_id ORDER BY timeinout)
AS next_movement
FROM my_table m
ORDER BY m.emp_id, m.timeinout
)
LOOP
IF v_emp_id IS NULL
OR v_emp_id != row.emp_id
OR row.timeinout >= v_first_movement c_shift_length THEN
v_emp_id := row.emp_id;
v_first_movement := row.timeinout;
v_previous_movement := NULL;
v_next_movement := row.next_movement;
v_check_status := 'FIRST_CHECK';
ELSIF row.next_movement >= v_first_movement c_shift_length
OR row.next_movement IS NULL THEN
v_previous_movement := row.previous_movement;
v_next_movement := NULL;
v_check_status := 'LAST_CHECK';
ELSE
v_previous_movement := row.previous_movement;
v_next_movement := row.next_movement;
v_check_status := 'DURING_DAY';
END IF;
PIPE ROW (
my_table_type(
row.emp_id,
row.timeinout,
row.flag,
v_previous_movement,
v_next_movement,
(v_next_movement - row.timeinout) DAY TO SECOND,
v_check_status
)
);
END LOOP;
END;
/
然后:
SELECT *
FROM TABLE(parse_my_table());
輸出:
EMP_ID CURRENT_MOVEMENT 旗幟 PREVIOUS_MOVEMENT NEXT_MOVEMENT 差異化 檢查狀態 1 2018-07-03T16:39:44 0 2018-07-04T01:14:40 00 08:34:56.000000 FIRST_CHECK 1 2018-07-04T01:14:40 1 2018-07-03T16:39:44 2018-07-04T01:14:44 00 00:00:04.000000 DURING_DAY 1 2018-07-04T01:14:44 1 2018-07-04T01:14:40 上次檢查 1 2018-07-04T16:14:52 0 2018-07-05T01:07:40 00 08:52:48.000000 FIRST_CHECK 1 2018-07-05T01:07:40 1 2018-07-04T16:14:52 2018-07-05T01:07:44 00 00:00:04.000000 DURING_DAY 1 2018-07-05T01:07:44 1 2018-07-05T01:07:40 上次檢查 1 2018-07-05T16:31:08 0 2018-07-06T01:01:48 00 08:30:40.000000 FIRST_CHECK 1 2018-07-06T01:01:48 1 2018-07-05T16:31:08 2018-07-06T01:01:52 00 00:00:04.000000 DURING_DAY 1 2018-07-06T01:01:52 1 2018-07-06T01:01:48 上次檢查 2 2018-07-03T08:37:40 0 2018-07-03T16:27:36 00 07:49:56.000000 FIRST_CHECK 2 2018-07-03T16:27:36 0 2018-07-03T08:37:40 上次檢查 2 2018-07-04T08:37:04 0 2018-07-04T12:58:36 00 04:21:32.000000 FIRST_CHECK 2 2018-07-04T12:58:36 0 2018-07-04T08:37:04 2018-07-04T13:09:48 00 00:11:12.000000 DURING_DAY 2 2018-07-04T13:09:48 0 2018-07-04T12:58:36 2018-07-04T17:15:32 00 04:05:44.000000 DURING_DAY 2 2018-07-04T17:15:32 0 2018-07-04T13:09:48 上次檢查
db<>在這里擺弄
uj5u.com熱心網友回復:
您可以使用MODEL子句:
SELECT emp_id,
current_movement,
flag,
previous_movement,
next_movement,
(next_movement - current_movement) DAY TO SECOND AS diff,
check_status
FROM (
SELECT m.*,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY timeinout) AS rn
FROM my_table m
)
MODEL
PARTITION BY (emp_id)
DIMENSION BY (rn)
MEASURES (
timeinout AS current_movement,
flag,
CAST(NULL AS DATE) AS first_movement,
CAST(NULL AS DATE) AS previous_movement,
CAST(NULL AS DATE) AS next_movement,
CAST(NULL AS VARCHAR2(11)) AS check_status
)
RULES (
first_movement[1] = current_movement[1],
first_movement[rn>1] = CASE
WHEN current_movement[cv(rn)] < first_movement[cv(rn)-1] INTERVAL '12' HOUR
THEN first_movement[cv(rn)-1]
ELSE current_movement[cv(rn)]
END,
previous_movement[1] = NULL,
previous_movement[rn>1] = CASE
WHEN current_movement[cv(rn)] < first_movement[cv(rn)-1] INTERVAL '12' HOUR
THEN current_movement[cv(rn)-1]
ELSE NULL
END,
next_movement[1] = current_movement[cv(rn) 1],
next_movement[rn>1] = CASE
WHEN current_movement[cv(rn) 1] < first_movement[cv(rn)] INTERVAL '12' HOUR
THEN current_movement[cv(rn) 1]
ELSE NULL
END,
check_status[1] = 'FIRST_CHECK',
check_status[rn>1] = CASE
WHEN first_movement[cv(rn)-1] != first_movement[cv(rn)]
THEN 'FIRST_CHECK'
WHEN current_movement[cv(rn) 1] < first_movement[cv(rn)] INTERVAL '12' HOUR
THEN 'DURING_DAY'
ELSE 'LAST_CHECK'
END
)
哪些輸出:
EMP_ID CURRENT_MOVEMENT 旗幟 PREVIOUS_MOVEMENT NEXT_MOVEMENT 差異化 檢查狀態 1 2018-07-03T16:39:44 0 2018-07-04T01:14:40 00 08:34:56.000000 FIRST_CHECK 1 2018-07-04T01:14:40 1 2018-07-03T16:39:44 2018-07-04T01:14:44 00 00:00:04.000000 DURING_DAY 1 2018-07-04T01:14:44 1 2018-07-04T01:14:40 上次檢查 1 2018-07-04T16:14:52 0 2018-07-05T01:07:40 00 08:52:48.000000 FIRST_CHECK 1 2018-07-05T01:07:40 1 2018-07-04T16:14:52 2018-07-05T01:07:44 00 00:00:04.000000 DURING_DAY 1 2018-07-05T01:07:44 1 2018-07-05T01:07:40 上次檢查 1 2018-07-05T16:31:08 0 2018-07-06T01:01:48 00 08:30:40.000000 FIRST_CHECK 1 2018-07-06T01:01:48 1 2018-07-05T16:31:08 2018-07-06T01:01:52 00 00:00:04.000000 DURING_DAY 1 2018-07-06T01:01:52 1 2018-07-06T01:01:48 上次檢查 2 2018-07-03T08:37:40 0 2018-07-03T16:27:36 00 07:49:56.000000 FIRST_CHECK 2 2018-07-03T16:27:36 0 2018-07-03T08:37:40 上次檢查 2 2018-07-04T08:37:04 0 2018-07-04T12:58:36 00 04:21:32.000000 FIRST_CHECK 2 2018-07-04T12:58:36 0 2018-07-04T08:37:04 2018-07-04T13:09:48 00 00:11:12.000000 DURING_DAY 2 2018-07-04T13:09:48 0 2018-07-04T12:58:36 2018-07-04T17:15:32 00 04:05:44.000000 DURING_DAY 2 2018-07-04T17:15:32 0 2018-07-04T13:09:48 上次檢查
db<>在這里擺弄
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/327535.html
標籤:sql oracle oracle10g analytic-functions
上一篇:在QlikSense中使用Kerberos身份驗證連接到Oracle
下一篇:什么是正確的連接格式?
