【问题标题】:Oracle how to select intervalsOracle如何选择区间
【发布时间】:2013-10-31 17:33:21
【问题描述】:

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

我的 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 个出口,则使用第二个,先忽略

【问题讨论】:

  • 您要实现哪些业务规则?如果用户打开门但没有退出,您如何确定他们何时真正退出?如果用户关注某人,您如何确定?如果您看到用户输入了时间 A 并在时间 B 再次输入,您如何确定第一个时间间隔何时结束(假设用户与其他人一起退出)?如果您看到用户在 A 处进入、在 B 处退出并在 C 处退出,您是否认为他们只是出现在 A 和 B 之间?你怎么知道他们是不走就开门还是和别人一起进来的?
  • 检查我提供的样本。这是相当描述性的。简而言之,如果连续有 2 个输入,则首先使用,然后忽略第二个。如果连续有 2 个出口,则使用第二个,请先忽略
  • 日期和时间没有存储在同一字段中的任何原因?
  • 启用不同日期的轻松时间过滤。有没有其他方法可以轻松实现?

标签: sql performance oracle date materialized-views


【解决方案1】:

我没有测试这是否有效,我忽略了 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;

【讨论】:

  • 在修复括号后,将employee_id 改为employeeid,它仍然无法正常工作 SQL 错误:ORA-25154:USING 子句的列部分不能有限定符
【解决方案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进行同样的测试

【讨论】:

  • 很高兴它为您工作!它首先将所有事件时间从入口映射到所有更大的事件时间从出口。然后对于每个进入时间,它需要最接近的退出时间。然后再次为每个退出时间它需要最接近的进入时间
猜你喜欢
  • 2012-10-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-15
  • 2017-06-12
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多