我有一个称为access_history的表,当员工向传感器出示访问卡时,该表会跟踪位置,卡号,日期时间。
[目前,员工使用时间卡来打卡和打卡,以记录每天的开始和结束时间。是的,我知道这是一种过时的方法。这就是为什么我被这个项目吸引来捕获这些数据并将其转换为考勤系统的原因。
我正在考虑使用触发器来完成此操作,但我认为有两个问题。首先,已经存在需要转换的数据。其次,如果触发器被禁用或删除,那么我将丢失数据并在同步时变得不可用。因此,我认为最好的方法是对数据进行爬网并创建出勤记录。
[作为DBA而不是开发人员,我认为最好的方法是使用合并语句(也称为upsert)。如果有记录,则更新它,否则插入它。顺便说一句,如果这不是最好的方法,那么我可以接受任何建议,包括设计变更。
请记住,我每天大约有1万名员工和大约一百万条历史记录,我需要仔细阅读这些记录,以便随时添加任何键,参数...
我知道可能会有很多插入和更新,因为我正在为每位员工更新emp_info表。避免这种情况的任何想法都很棒。
注意,emp_attendance表中可能有一对以上的开始日期和结束日期,因为人们可能会出去吃午餐或去另一座建筑物。其次,由于我们是24x7全天候,结束日期可以是午夜。在我的示例测试案例中,我提供了这种情况的示例。
我希望有人能提供一些代码或让我开始如何遍历和配对数据。
我知道我需要在emp_histoty表上查询,其中location_type = T,并查看date> last / start_date是否为employees表
任何人都非常感谢我们。更多细节和细节将是很棒的。感谢所有回答。
-- 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
);
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
--- next run find only records >
-- last_start_date from employee
-- table and traverse
-- access_history to pair records
-- for emp_attendance table
这里是“主查询”,给出输入和您的问题描述,它将返回配对。可以将其转换为视图(可能是具体化的视图,具体取决于您的需求),也可以将其用于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 not in ( select location_id
from locations
where location_type = 'A'
)
)
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