1

想象一下有锁门的房间。门口有读卡器。要打开门,您需要将卡放入读卡器。

我的 Oracle 数据库中有 2 个包含事件的表 - 条目(门是从房间外打开的)出口(门是从房间内打开的)

我想要的只是选择存在间隔,例如员工 1 在某天从 10:00 到 11:00 以及从 12:00 到 18:00 在房间里。

但是有3个问题

  1. 有时用户打开门但不离开房间。
  2. 有时一个用户打开门,另一位用户和他一起出去,而门打开一次。
  3. 数据大小(每张表约100k)

和问题:

  1. 选择存在间隔的最佳方法是什么
  2. 有没有办法创建快速可刷新的物化视图来解决这个问题?

这是样本

drop table entries;
drop table exits;

CREATE TABLE ENTRIES
(   
  "EVENTDATE" DATE NOT NULL, 
  "EVENTTIME" DATE NOT NULL, 
  "EMPLOYEEID" NUMBER NOT NULL
);

CREATE TABLE EXITS
(   
  "EVENTDATE" DATE NOT NULL, 
  "EVENTTIME" DATE NOT NULL, 
  "EMPLOYEEID" NUMBER NOT NULL
);


delete from ENTRIES;
delete from exits;

Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 08:44:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:18:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:19:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:22:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:37:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:38:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:39:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:40:00','DD-MON-RR HH24:MI:SS'));
Insert into ENTRIES (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 13:22:00','DD-MON-RR HH24:MI:SS'));

Insert into EXITS   (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:40:00','DD-MON-RR HH24:MI:SS'));
Insert into EXITS   (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 12:36:00','DD-MON-RR HH24:MI:SS'));
Insert into EXITS   (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 11:55:00','DD-MON-RR HH24:MI:SS'));
Insert into EXITS   (EMPLOYEEID,EVENTDATE,EVENTTIME) values (8,to_date('01-AUG-13 00:00:00','DD-MON-RR HH24:MI:SS'),to_date('01-JAN-00 18:02:00','DD-MON-RR HH24:MI:SS'));

期望的结果是这样的:

"EMPLOYEID"   "EVENTDATE"            "ENTERTIME"              "LEAVETIME"
8            01-AUG-13 00:00:00    01-JAN-00 08:44:00      01-JAN-00 11:55:00
8            01-AUG-13 00:00:00    01-JAN-00 12:18:00      01-JAN-00 12:36:00
8            01-AUG-13 00:00:00    01-JAN-00 12:37:00      01-JAN-00 12:40:00
8            01-AUG-13 00:00:00    01-JAN-00 13:22:00      01-JAN-00 18:02:00

更新

如果连续有 2 个输入,请先使用,然后忽略第二个。如果连续有 2 个出口,请使用第二个,首先忽略

4

2 回答 2

2

请找到以下经过测试的查询:-

SELECT employeeid,eventdate,entry_time,exit_time FROM (SELECT employeeid,eventdate,entry_time,exit_time, rank() over (partition BY employeeid,eventdate,exit_time ORDER BY entry_time ASC) et FROM (SELECT t.employeeid,t.eventdate,t.eventtime entry_time, o.eventtime exit_time, rank() over (partition BY t.employeeid,t.eventdate,t.eventtime ORDER BY o.eventtime ASC) mt FROM entries t,exits o WHERE t.employeeid = o.employeeid AND t.eventdate=o.eventdate AND t.eventtime < o.eventtime) WHERE mt =1)WHERE et=1

您可以在http://sqlfiddle.com/#!4/72ac2/9进行相同的测试

于 2013-10-23T10:59:08.293 回答
0

我没有测试这是否有效,我忽略了 eventtime 但尝试以下操作。基本上,我们想要找到真正的进入和真正的退出(真正的进入:进入/退出之间没有进入,真正的退出:退出/进入之间没有退出)。我们加入两者(加入两次真正的出口)并显示进入/退出,其中退出在进入之后并且在进入退出之间没有其他出口。

select
  a.employee_id
, true_enterdate
, true_exitdate
from (
select
  ent1.eventdate true_enterdate  -- enter/exit with no enter between
, employee_id
from
  entries ent1
  join exits ex1 using (employee_id)
  left outer join entries ent2 using (employee_id)
where 1=1
  and ent1.eventdate < ex1.eventdate  
  and ent2.eventdate > ent1.eventdate
  and ent2.eventdate < ex1.eventdate
  and ent2.employee_id is null 
) a join (  
select
  ent1.eventdate true_exitdate  -- exit/enter with no exit between
, employee_id
from
  entries ent1
  join exits ex1 using (employee_id)
  left outer join exits ex2 using (employee_id)
where 1=1
  and ent1.eventdate < ex1.eventdate  
  and ex2.eventdate > ent1.eventdate
  and ex2.eventdate < ex1.eventdate
  and ex2.employee_id is null 
) b using (employee_id)
left outer join (
select
  ent1.eventdate true_exitdate2  -- exit/enter with no exit between
, employee_id
from
  entries ent1
  join exits ex1 using (employee_id)
  left outer join exits ex2 using (employee_id)
where 1=1
  and ent1.eventdate < ex1.eventdate  
  and ex2.eventdate > ent1.eventdate
  and ex2.eventdate < ex1.eventdate
  and ex2.employee_id is null 
) c using (employee_id)(
where 1=1
  and true_enterdate < true_exitdate
  and true_exitdate2 > true_enterdate
  and true_exitdate2 < true_exitdate
  and c.employee_id is null;
于 2013-10-22T18:53:20.910 回答