Sql Oracle按日期范围查询,但如果需要,可获取此范围之外的后续记录

Sql Oracle按日期范围查询,但如果需要,可获取此范围之外的后续记录,sql,oracle,Sql,Oracle,我有一个员工时钟数据的db表,我想每天查询这些数据。但是,当员工的轮班时间跨越午夜时,查询将错过确定该员工总小时数所需的事务 ID NUMBER (PK) EmpId VARCHAR TransActType VARCHAR Start TIMESTAMP Account VARCHAR 1 EmpA ClockIn 7/7/20 8am Account1 2 EmpB ClockIn 7/7/20 9am Account7 3 EmpC ClockIn 7/7/20 9

我有一个员工时钟数据的db表,我想每天查询这些数据。但是,当员工的轮班时间跨越午夜时,查询将错过确定该员工总小时数所需的事务

ID NUMBER (PK)
EmpId VARCHAR
TransActType VARCHAR
Start TIMESTAMP
Account VARCHAR

1   EmpA ClockIn  7/7/20 8am  Account1
2   EmpB ClockIn  7/7/20 9am  Account7
3   EmpC ClockIn  7/7/20 9am  Account1
4   EmpA Switch   7/7/20 10am Account3
5   EmpA Switch   7/7/20 11am Account6
6   EmpC Switch   7/7/20 1pm  Account4
7   EmpC ClockOut 7/7/20 3pm 
8   EmpD ClockIn  7/7/20 5pm  Account5
9   EmpD Switch   7/7/20 6pm  Account6
10  EmpB ClockOut 7/7/20 6pm
11  EmpD Switch   7/7/20 7pm  Account4
12  EmpA ClockOut 7/7/20 8pm
13  EmpD Switch   7/8/20 1am  Account3
14  EmpD ClockOut 7/8/20 2am
15  EmpA ClockIn  7/8/20 8am  Account1
...
我对7/7/20的劳动力需求是

SELECT * FROM labor li where li.start between 7/7/20 12 am and 7/7/20 11:59 pm order by empId, start
只会拿到记录1-12,但也应该拿到记录13和记录14

我的应用程序代码通过循环查询结果和区分连续员工事务的开始时间来计算每个员工每天收取的帐户持续时间

如果没有记录13和14,我无法确定员工D的account4和account3持续时间

也许我的数据库设计有缺陷,因为我只存储事务的开始时间,然后使用应用程序代码逻辑计算持续时间。我决定这样设计,这样如果记录被移动/插入/删除,那么如果结束时间也被持久化,我个人持久化的员工劳动项目重叠的机会就会减少。上述人工数据样本显示,员工可以全天更换账户,每天有多笔人工交易

我希望有一个查询能够在每个员工的基础上进行前瞻,如果该员工的最后一笔交易不是时间戳范围内的“打卡”,则继续抓取记录,直到找到为止

出于同样的原因,我不能让该员工的第一笔交易是前一天的打卡

如果这些查询几乎是不可能的,并且数据库设计应该更改,那么我想知道这一点。

我希望查询能够在每个员工的基础上进行前瞻,如果该员工的最后一笔交易不是时间戳范围内的“打卡”,则继续抓取记录,直到找到为止。

我希望查询能够在每个员工的基础上进行时间前瞻,如果该员工的最后一笔交易不是时间戳范围内的“打卡”,则继续抓取记录,直到找到为止。


听起来你想抵消白天的时间,所以从凌晨2:00到8:00之间的某个时间段开始,不知道确切的时间,比如说凌晨5:00

只需减去5小时进行日期比较。要么:

where li.start - interval '5' hour >= date '2020-07-07' and 
      li.start - interval '5' hour < date '2020-07-08'
其中li.start-间隔“5”小时>=日期“2020-07-07”和
li.启动-间隔“5”小时<日期“2020-07-08”
或者,以更方便索引的方式:

where li.start >= date '2020-07-07' + interval '5' hour 
      li.start < date '2020-07-08' + interval '5' hour
其中li.start>=日期'2020-07-07'+间隔'5'小时
li.开始<日期'2020-07-08'+间隔'5'小时

听起来你想抵消白天的时间,所以从凌晨2:00到8:00之间的某个时间点开始,不知道确切的位置,比如说凌晨5:00

只需减去5小时进行日期比较。要么:

where li.start - interval '5' hour >= date '2020-07-07' and 
      li.start - interval '5' hour < date '2020-07-08'
其中li.start-间隔“5”小时>=日期“2020-07-07”和
li.启动-间隔“5”小时<日期“2020-07-08”
或者,以更方便索引的方式:

where li.start >= date '2020-07-07' + interval '5' hour 
      li.start < date '2020-07-08' + interval '5' hour
其中li.start>=日期'2020-07-07'+间隔'5'小时
li.开始<日期'2020-07-08'+间隔'5'小时

我想这正是你想要的:

我不相信这是最好的解决方案,但也许你可以用它来构建你的实际解决方案。可能思考Stibbons的答案会比这更好,如果能够适应评论中的需求澄清(“所以不,如果当天是7/8/20,我不想选择13和14”)

与
erange AS
(
选择lii.EmpId,lii.“开始”,MIN(lio.“开始”)作为“结束”
从劳工lii内部加入劳工lio
关于lii.EmpId=lio.EmpId
其中lii.transactionType='ClockIn'
和lio.TransactionType='ClockOut'
lii.“开始”
我想这正是你想要的:

我不相信这是最好的解决方案,但也许你可以用它来构建你的实际解决方案。可能思考Stibbons的答案会比这更好,如果能够适应评论中的需求澄清(“所以不,如果当天是7/8/20,我不想选择13和14”)

与
erange AS
(
选择lii.EmpId,lii.“开始”,MIN(lio.“开始”)作为“结束”
从劳工lii内部加入劳工lio
关于lii.EmpId=lio.EmpId
其中lii.transactionType='ClockIn'
和lio.TransactionType='ClockOut'
lii.“开始”
如果您使用的是最新版本的Oracle,您可以使用
match_recognize()
跟踪时钟输入/切换/时钟输出,然后根据时钟输入时间进行过滤:

select id, empid, transacttype, start_time, account
from labor
match_recognize (
  partition by empid
  order by start_time
  measures
    first(start_time) as grp_clockin
  all rows per match
  after match skip past last row
  pattern (clockin switch* clockout*)
  define
    clockin as clockin.transacttype = 'ClockIn',
    switch as switch.transacttype = 'Switch',
    clockout as clockout.transacttype = 'ClockOut'
)
where grp_clockin >= date '2020-07-07'
and grp_clockin < date '2020-07-08'
order by empid, grp_clockin, start_time;
由于筛选发生较晚,您可以使用内联视图将其缩小,至少缩小到最小日期/时间:

select id, empid, transacttype, start_time, account
from (
  select *
  from labor
  where start_time >= date '2020-07-07'
)
match_recognize (
  partition by empid
  order by start_time
  measures
    first(start_time) as grp_clockin
  all rows per match
  after match skip past last row
  pattern (clockin switch* clockout*)
  define
    clockin as clockin.transacttype = 'ClockIn',
    switch as switch.transacttype = 'Switch',
    clockout as clockout.transacttype = 'ClockOut'
)
where grp_clockin >= date '2020-07-07'
and grp_clockin < date '2020-07-08'
order by empid, grp_clockin, start_time;

如果您使用的是最新版本的Oracle,您可以使用
匹配识别()
跟踪时钟输入/切换/时钟输出,然后根据时钟输入时间进行过滤:

select id, empid, transacttype, start_time, account
from labor
match_recognize (
  partition by empid
  order by start_time
  measures
    first(start_time) as grp_clockin
  all rows per match
  after match skip past last row
  pattern (clockin switch* clockout*)
  define
    clockin as clockin.transacttype = 'ClockIn',
    switch as switch.transacttype = 'Switch',
    clockout as clockout.transacttype = 'ClockOut'
)
where grp_clockin >= date '2020-07-07'
and grp_clockin < date '2020-07-08'
order by empid, grp_clockin, start_time;
由于筛选发生较晚,您可以使用内联视图将其缩小,至少缩小到最小日期/时间:

select id, empid, transacttype, start_time, account
from (
  select *
  from labor
  where start_time >= date '2020-07-07'
)
match_recognize (
  partition by empid
  order by start_time
  measures
    first(start_time) as grp_clockin
  all rows per match
  after match skip past last row
  pattern (clockin switch* clockout*)
  define
    clockin as clockin.transacttype = 'ClockIn',
    switch as switch.transacttype = 'Switch',
    clockout as clockout.transacttype = 'ClockOut'
)
where grp_clockin >= date '2020-07-07'
and grp_clockin < date '2020-07-08'
order by empid, grp_clockin, start_time;

您可以使用简单的分析函数“第一个值”来表示:

选择li*
,第一个_值(解码(TransacticType,'ClockOut','START')忽略空值)
结束(按EmpId顺序分区,按当前行和无界后续行之间的“开始”行)打卡
来自劳工部的李;
如您所见,它添加了新的列ClockOut,其中包含下一次ClockOut的时间,因此您可以轻松地将所需数据添加到查询中

完整测试用例:

——示例数据:
使用人工(ID、EmpId、TransactionType、“开始”、帐户
select id, empid, transacttype, start_time, account
from (
  select *
  from labor
  where start_time >= date '2020-07-07'
  and start_time < date '2020-07-09'
)
...
select empid, grp_start_time, grp_end_time, grp_account,
  (grp_end_time - grp_start_time) * interval '1' day as elapsed
from labor
match_recognize (
  partition by empid
  order by start_time
  measures
    first(start_time) as grp_start_time,
    first(account) as grp_account,
    final last(start_time) as grp_end_time
  one row per match
  after match skip to last grp_end
  pattern (grp_start grp_end)
  define
    grp_start as grp_start.transacttype in ('ClockIn', 'Switch'),
    grp_end as grp_end.transacttype in ('Switch', 'ClockOut')
)
where grp_start_time >= date '2020-07-07'
and grp_start_time < date '2020-07-08'
order by empid, grp_start_time;
EMPID | GRP_START_TIME      | GRP_END_TIME        | GRP_ACCOUNT | ELAPSED                      
:---- | :------------------ | :------------------ | :---------- | :----------------------------
EmpA  | 2020-07-07 08:00:00 | 2020-07-07 10:00:00 | Account1    | +000000000 02:00:00.000000000
EmpA  | 2020-07-07 10:00:00 | 2020-07-07 11:00:00 | Account3    | +000000000 01:00:00.000000000
EmpA  | 2020-07-07 11:00:00 | 2020-07-07 20:00:00 | Account6    | +000000000 09:00:00.000000000
EmpB  | 2020-07-07 09:00:00 | 2020-07-07 18:00:00 | Account7    | +000000000 09:00:00.000000000
EmpC  | 2020-07-07 09:00:00 | 2020-07-07 13:00:00 | Account1    | +000000000 04:00:00.000000000
EmpC  | 2020-07-07 13:00:00 | 2020-07-07 15:00:00 | Account4    | +000000000 02:00:00.000000000
EmpD  | 2020-07-07 17:00:00 | 2020-07-07 18:00:00 | Account5    | +000000000 01:00:00.000000000
EmpD  | 2020-07-07 18:00:00 | 2020-07-07 19:00:00 | Account6    | +000000000 01:00:00.000000000
EmpD  | 2020-07-07 19:00:00 | 2020-07-08 01:00:00 | Account4    | +000000000 06:00:00.000000000
|   ID | EMPI | TRANSACT | START               | ACCOUNT  | CLOCKOUT
| ---- | ---- | -------- | ------------------- | -------- | -------------------
|    1 | EmpA | ClockIn  | 2020-07-07 08:00:00 | Account1 | 2020-07-07 20:00:00
|    4 | EmpA | Switch   | 2020-07-07 10:00:00 | Account3 | 2020-07-07 20:00:00
|    5 | EmpA | Switch   | 2020-07-07 11:00:00 | Account6 | 2020-07-07 20:00:00
|   12 | EmpA | ClockOut | 2020-07-07 20:00:00 |          | 2020-07-07 20:00:00
|   15 | EmpA | ClockIn  | 2020-07-08 08:00:00 | Account1 | 
|    2 | EmpB | ClockIn  | 2020-07-07 09:00:00 | Account7 | 2020-07-07 18:00:00
|   10 | EmpB | ClockOut | 2020-07-07 18:00:00 |          | 2020-07-07 18:00:00
|    3 | EmpC | ClockIn  | 2020-07-07 09:00:00 | Account1 | 2020-07-07 15:00:00
|    6 | EmpC | Switch   | 2020-07-07 13:00:00 | Account4 | 2020-07-07 15:00:00
|    7 | EmpC | ClockOut | 2020-07-07 15:00:00 |          | 2020-07-07 15:00:00
|    8 | EmpD | ClockIn  | 2020-07-07 17:00:00 | Account5 | 2020-07-08 02:00:00
|    9 | EmpD | Switch   | 2020-07-07 18:00:00 | Account6 | 2020-07-08 02:00:00
|   11 | EmpD | Switch   | 2020-07-07 19:00:00 | Account4 | 2020-07-08 02:00:00
|   13 | EmpD | Switch   | 2020-07-08 01:00:00 | Account3 | 2020-07-08 02:00:00
|   14 | EmpD | ClockOut | 2020-07-08 02:00:00 |          | 2020-07-08 02:00:00