sql—oracle记录配对的最佳方法

dsf9zpds  于 2021-07-26  发布在  Java
关注(0)|答案(1)|浏览(388)

我有一个名为access\u history的表,它跟踪员工向传感器出示访问卡的位置、卡号和日期时间。
目前,员工每天都用考勤卡打卡,记录上班时间和下班时间。是的,我知道那是一种非常过时的方法。这就是为什么我被抛出这个项目来捕获这些数据,并将其转换为一个时间和出勤系统。
我在考虑使用触发器来实现这一点,但我认为有两个问题。首先,已有数据需要转换。第二,如果触发器被禁用或删除,我将丢失数据,并在同步时变为out。所以我认为最好的方法是浏览数据并创建出勤记录。
作为一个dba,而不是一个开发人员,我想最好的方法是使用merge语句,也就是upsert。如果记录在那里,请更新它,否则请插入它。顺便说一句,如果这不是最好的方法,我愿意接受任何和所有的建议,包括设计更改。
请记住,我有大约1万名员工和大约一百万以上的历史记录,每天,我需要扫描,所以请随意添加任何钥匙,partitions…
我知道可能有很多插入和更新,因为我正在为每个员工更新emp\u info表。任何关于避免这种情况的想法都是很好的。
请注意,emp\ U考勤表中可能有1对以上的开始和结束日期,因为人们可能会出去吃午饭或去不同的建筑。其次,由于我们是全天候的,所以结束日期可以跨越午夜。在我的示例测试用例中,我提供了一个这种情况的示例。
我希望有人能给我提供一些代码或让我开始如何遍历和配对的数据。
我知道我需要查询emp\u history表,其中location\u type=t,并查看employees表中的date>last/start\u date
感谢所有帮助我们的人。更多的细节和细节会更好。感谢所有的回答。

-- Drop table emp_info purge:
-- Drop table locations purge;
-- Drop table access_histoty purge;
-- Drop table emp_attendance purge;

CREATE TABLE employees
(
   employee_id NUMBER(6),
   first_name VARCHAR2(20),
   last_name VARCHAR2(25) NOT NULL,
   card_num varchar2(10)  NOT NULL,
   Last_start_date DATE
);

ALTER TABLE employees
     ADD ( CONSTRAINT employee_id_pk
   PRIMARY KEY (employee_id));

Insert into employees values (1, 'Mike', 'Jones', 'AAA1', NULL);

Insert into employees values (2, 'Jane', 'Doe', 'BBB2', NULL);

Insert into employees values (3, 'Paul', 'Smith', 'CCC3', NULL);

Insert into employees values (4, 'John', 'Henry', 'DDD4', NULL);

 Create table locations(
   location_id NUMBER(4),
   location_name varchar2(30),
   location_type char(1));

 -- A=access T=Time & Attendance 

ALTER TABLE locations 
     ADD ( CONSTRAINT lication_id_pk
   PRIMARY KEY (location_id));

Insert into locations values (101, 'South Front Door 1', 'T');

  Insert into locations values (102, 'South Front Door 2', 'T');

  Insert into locations values (103, 'East Back Door 1', 'T');

   Insert into locations values (104, 'East Back Door 2', 'T');

   Insert into locations values (105,'Computer Room', 'A');

   Insert into locations values (106,'1st Floor North',  'A');

Create table access_history(
   employee_id NUMBER(6), 
   card_num varchar2(10),
   location_id number(4),
   Access_date date
);

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (1, 'AAA1', 101,    TO_DATE('06212020 21:02:04',  'MMDDYYYY HH24:MI:SS'));

   -- TYpe T no previous data for this
   -- empid record INSERT empid, 
    -- start  time ONLY in table below 
     -- and  update last_start_date  
     -- with DATETIME.

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (1, 'AAA1', 102,    TO_DATE('06212020 23:52:14',  'MMDDYYYY HH24:MI:SS'));

  -- Type T record empid, start_time
  --  set update end_time only in
  -- emp_attendance.

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (2, 'BBB2', 103,    TO_DATE('06212020 08:32:35',  'MMDDYYYY HH24:MI:SS'));

 -- TYpe T  INSERT empid, start 
 -- time ONLY in emp_attendance.  
 -- update last_start_date with 
 --  DATETIME on emp_info table

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (2, 'BBB2', 102,    TO_DATE('06212020 15:39:05',  'MMDDYYYY HH24:MI:SS'));

-- Type T record empid, start_time 
--  set, update end_time only in
-- emp_attendance.

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (3, 'CCC3', 103,    TO_DATE('06212020 15:39:05',  'MMDDYYYY HH24:MI:SS'));

 -- TYpe T  INSERT empid, start 
 -- time ONLY in emp_attendance.  
 -- update last_start_date with 
 --  DATETIME on emp_info table

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (3, 'CCC3', 105,    TO_DATE('06212020 18:19:55',  'MMDDYYYY HH24:MI:SS'));

-- Type A record don't do anything to 
-- emp_attendance.

INSERT INTO access_history
( employee_id, card_num,
   location_id, Access_date )
 VALUES (3, 'CCC3', 104,    TO_DATE('06222020 04:04:35',  'MMDDYYYY HH24:MI:SS'));

-- Type T record empid, start_time 
--  set, update end_time only in
-- emp_attendance.

-- After the first run the output 
-- should.   look like this:

 CREATE TABLE  emp_attendance    
  (employee_id NUMBER(6),
   start_date DATE,
   end_date DATE
   create_date DATE
   );

  Create sequence emp_attendance_seq;

insert into emp_attendance (seq_num, employee_id, start_date, end_Date)
   with
   prep (employee_id, start_date, rn, end_date) as (
     select employee_id, access_date
        , row_number()      over (partition by card_num order by access_date)
      , lead(access_date) over (partition by card_num order by access_date)
   from   access_history
 where  location_id in ( select location_id
                     from   locations
                      where  location_type = 'T'
                  )
  )
  select 
    emp_attendance_seq.nextval,
      employee_id,
     start_date,
     nvl(end_date, start_date)
    from prep
   where  mod(rn, 2) = 1;

1  06212020.   06212020  SYSDATE
    21:02:04.     23:52:14

2   06212020.  06212020  SYSDATE
 08:32:35        15:39:05 

3  06212020   06222020.  SYSDATE 
    15:39:05     04:04:35

  --- changes
  -- INSERT records
  -- if end_date is NULL set to start date
    --  Added sequence number to row for unique identification
qcuzuvrc

qcuzuvrc1#

这里是“main query”,它将返回给定输入和问题描述的配对。可以将其转换为视图(可能是具体化视图,具体取决于您的需要),也可以将其用于 MERGE 声明。
关于将此数据存储在表中的警告(例如 EMP_ATTENDANCE ,用维护 MERGE 语句或其他方式):如果您需要更正输入表中的数据(例如,传感器未正确读取卡,并且您必须在事件发生五天后删除一行或插入一行),则从该日期/时间开始的所有相关员工的所有配对都将完全混乱。“开始日期”现在可能变成“结束日期”,反之亦然;现在还不清楚 MERGE 为了解释这种情况,将写一份声明。
不管怎样,这里是“主要查询”。主要工作在子查询中;它使用两个解析函数,但它们使用相同的函数 partition by 以及 order by 标准,所以工作实际上只做一次。外部查询只是应用一个过滤器,以便只保留每隔一行。这应该比只创建行号(而不是 LEAD 功能)和应用 PIVOT . 这是因为 PIVOT 是一个昂贵的操作(“排序”),无论如何,它仍然需要首先使用分析函数。如果我们必须计算 ROW_NUMBER ,我们得到 LEAD 就像我刚才解释的,基本上是免费的。

with
  prep (employee_id, start_date, rn, end_date) as (
    select employee_id, access_date
         , row_number()      over (partition by card_num order by access_date)
         , lead(access_date) over (partition by card_num order by access_date)
    from   access_history
    where  location_id in ( select location_id 
                            from   locations 
                            where  location_type = 'T'
                          )
  )
select employee_id, start_date, end_date
from   prep
where  mod(rn, 2) = 1
;

EMPLOYEE_ID START_DATE          END_DATE           
----------- ------------------- -------------------
          1 2020-06-21 21:02:04 2020-06-21 23:52:14
          2 2020-06-21 08:32:35 2020-06-21 15:39:05
          3 2020-06-21 15:39:05 2020-06-22 04:04:35

相关问题