我从设备的指纹日志中获取了此文件:
Id User_id PuchTime
--------------------------
1 152 2018-07-17 09:38:03
2 184 2018-07-17 16:56:43
3 152 2018-07-17 16:57:18
4 165 2018-07-17 16:57:43
5 70 2018-07-17 16:57:59
6 134 2018-07-17 16:58:28
7 276 2018-07-17 16:59:04
8 278 2018-07-17 16:59:05
9 271 2018-07-17 16:59:10
10 268 2018-07-17 16:59:13
11 284 2018-07-17 16:59:16
12 364 2018-07-17 16:59:35
13 19 2018-07-17 16:59:38
14 381 2018-07-17 17:01:12
15 73 2018-07-17 17:12:31
16 126 2018-07-17 17:12:36
17 382 2018-07-17 17:13:50
18 53 2018-07-18 06:34:13
19 284 2018-07-18 08:05:17
如何在Postgres查询中进行查询以提取数据,如下所示:
User_id Check_Date TimeIN TimeOUT Hours status
--------------------------------------------------------
152 2018-07-17 09:38:03 16:56:43 7.8 present
152 2018-07-18 Absent
我使用此查询
SELECT userid, name, CAST(PuchTime as DATE) Check_Date,
to_char(PuchTime, 'day') days,
MIN(CAST(PuchTime as Time)) TimeIN,
MAX(Cast(PuchTime as Time)) TimeOUT,
CAST(MAX(PuchTime) - MIN(PuchTime) AS Time) As hour
FROM attendance_FHLHR
GROUP BY userid,name, CAST(PuchTime as DATE), to_char(PuchTime, 'day')
order by name DESC, check_date ASC, userid ASC
Output of my query:
我需要状态和小时数计算。
User_id Check_Date TimeIN TimeOUT Hours
-----------------------------------------------
152 2018-07-17 09:38:03 16:56:43 7:18:40
152 2018-07-18
答案 0 :(得分:1)
免责声明 :(这两个都适用:这里和this one都这样)
WITH dates AS( -- 1
SELECT
min(checktime)::date as min,
max(checktime)::date as max
FROM log
)
SELECT
user_id,
check_date::date,
-- 4:
CASE WHEN checktime::date = check_date THEN checktime::time ELSE NULL END as time_in,
CASE WHEN checktime::date = check_date THEN time_out::time ELSE NULL END as time_out,
CASE WHEN checktime::date = check_date THEN (time_out - checktime)::time ELSE NULL END as hours
FROM (
SELECT
user_id,
checktime,
lead(checktime) OVER (ORDER BY checktime) as time_out, -- 2
generate_series( -- 3
(SELECT min FROM dates),
(SELECT max FROM dates),
interval '1 day'
) as check_date
FROM log
)s
ORDER BY user_id, check_date
lead
window function将下一个checktime
值带到当前行。因此下一个checktime
被计为time_out
generate_series
生成给定(计算)范围之间的所有日期值CASE
部分:如果当前checktime
不等于生成的日期值,则给出NULL
;否则给出当前的time_in
/ time_out
/时差
演示,请参见上方的小提琴的第二部分
WITH dates AS(
SELECT
min(checktime)::date as min,
max(checktime)::date as max
FROM log
)
SELECT DISTINCT ON (user_id, check_date, time_in) -- 6
user_id,
check_date,
to_char(check_date, 'Day') as day, -- 2
COALESCE(time_in, -- 4
MAX(time_in) OVER (PARTITION BY user_id, check_date ORDER BY time_out NULLS LAST)
) as time_in,
time_out,
hours,
CASE -- 5
WHEN checktime::date = check_date THEN 'present'
WHEN of.days IS NOT NULL THEN 'OFF DAY'
ELSE 'absent'
END as status
FROM (
SELECT
user_id,
check_date,
checktime,
CASE WHEN checktime::date = check_date THEN checktime::time ELSE NULL END as time_in,
CASE WHEN checktime::date = check_date THEN time_out::time ELSE NULL END as time_out,
-- 1
CASE WHEN checktime::date = check_date THEN extract(epoch FROM (time_out - checktime)) / 60 / 60 ELSE NULL END as hours
FROM (
SELECT
user_id,
checktime,
lead(checktime) OVER (ORDER BY checktime) as time_out,
generate_series(
(SELECT min FROM dates),
(SELECT max FROM dates),
interval '1 day'
) as check_date
FROM log
) s
) s
--- 3
LEFT JOIN off_days of ON (of.userid = s.user_id) AND (of.days = trim(to_char(check_date, 'day')))
ORDER BY user_id, check_date
因为这是上一个查询的扩展,所以我仅解释更改:
time
而不是numeric
。因此extract(epoch...)
会得到差值的秒数,/ 60 / 60
会转换成小时数to_char
功能user_id
和工作日加入休假表(再次使用to_char
函数,这次使用小写字母)。 to_char
adds whitespace-因此trim()
将其删除以进行比较DISTINCT
和工作日进行简单的user_id
,因为152
一天有两个条目。但是,由于53
在两个日期的不同日期都有两个条目(在我的示例中,显示小提琴),因此两个日期都有效,并且创建了一个空行。此代码行将time_in
值复制到空行中(下一步,请参阅(6))present
。如果不是,请检查是否是“休息日”,否则absent
distinct
。这也适用于(C),因为我们在(4)中的time_in
行中重复了NULL
答案 1 :(得分:0)
您需要一组参考日期和用户来比较活动。在任何编程语言中,生成该函数都不是一件容易的事,对于SQL而言尤其如此。但是,您可以使用已经拥有的日期/用户列表,并通过笛卡尔加入用户列表:
SELECT alldatesusers.userid, alldatesusers.ref_date AS check_date
, TimeIn, TimeOut, hour
FROM
(SELECT DISTINCT CAST(rd.PuchTime as DATE) AS refdate, user.userid
FROM attendance_FHLHR rd
, (SELECT DISTINCT ru.userid
FROM attendance_FHLHR ru) AS user
) AS alldatesusers
LEFT JOIN
( SELECT userid, name, CAST(PuchTime as DATE) Check_Date
,to_char(PuchTime, 'day') days
,MIN(CAST(PuchTime as Time)) TimeIN
,MAX(Cast(PuchTime as Time)) TimeOUT
,CAST(MAX(PuchTime) - MIN(PuchTime) AS Time) As hour
FROM attendance_FHLHR
GROUP BY userid,name, CAST(PuchTime as DATE), to_char(PuchTime, 'day')
) AS attendance
ON alldatesusers.refdate=attendance.Check_date
AND alldates.userid=attendance.userid
答案 2 :(得分:0)
以下SQL使用generate_series()
创建日历,并将打孔卡表中的现有工作程序列表用作用户列表。通过交叉加入,可以列出日期和用户,您可以从中轻松地得出缺席/在场的信息。
将练习的分钟数转换为小数小时留给读者练习。
with workers as ( select distinct user_id from clock)
, calendar as (
select workday from
generate_series(
(date '2018-07-01')::timestamp,
(date '2018-07-31')::timestamp,
interval '1 day') workday
)
SELECT w.user_id, workday,
to_char(cast(PuchTime as DATE), 'day') days,
MIN(CAST(PuchTime as Time)) TimeIN,
MAX(Cast(PuchTime as Time)) TimeOUT,
CAST(MAX(PuchTime) - MIN(PuchTime) AS Time) As hour
FROM
calendar cross join workers w
left join clock c
on workday = CAST(c.PuchTime as DATE)
and w.user_id=c.user_id
GROUP BY w.user_id, workday, calendar.*, CAST(c.PuchTime as DATE)
order by w.user_id DESC, calendar ASC
user_id workday days timein timeout hour
382 2018-07-01T00:00:00Z (null) (null) (null) (null)
382 2018-07-02T00:00:00Z (null) (null) (null) (null)
382 2018-07-03T00:00:00Z (null) (null) (null) (null)
382 2018-07-04T00:00:00Z (null) (null) (null) (null)
382 2018-07-05T00:00:00Z (null) (null) (null) (null)
...
152 2018-07-16T00:00:00Z (null) (null) (null) (null)
152 2018-07-17T00:00:00Z tuesday 09:38:03 16:57:18 07:19:15
152 2018-07-18T00:00:00Z (null) (null) (null) (null)
152 2018-07-19T00:00:00Z (null) (null) (null) (null)
...
答案 3 :(得分:0)
假设每个员工都没有出现在同一天,这应该可以工作:
with from_thru as (
select
min (punchtime)::date as from_date,
max (punchtime)::date as thru_date
from attendance_FHLHR
),
users as (
select distinct user_id from attendance_FHLHR
)
select
gs.date::date, u.user_id, min (a.punchtime) as TimeIn,
max (a.punchtime) as TimeOut,
extract (epoch from max (a.punchtime) - min (a.punchtime))/3600 as Hours,
case
when min (a.punchtime) is null then 'Absent'
when count (1) = 1 then 'Missing Punch'
else 'Present'
end as status
from
from_thru
cross join generate_series (from_date, thru_date, interval '1 day') gs (date)
cross join users u
left join attendance_FHLHR a on
a.punchtime::date = gs.date and
a.user_id = u.user_id
group by
gs.date, u.user_id
在只有一个拳的情况下,我还添加了一个称为“缺少拳”的条件。
-编辑11/2/2018-
根据您对“休息日”表的反馈,以下是我认为也可以解决的问题。请注意,无需更改子查询:
select
gs.date::date, u.user_id, min (a.checktime) as TimeIn,
max (a.checktime) as TimeOut,
extract (epoch from max (a.checktime) - min (a.checktime))/3600 as Hours,
case
when o.userid is not null then 'Off Day'
when min (a.checktime) is null then 'Absent'
when count (1) = 1 then 'Missing Punch'
else 'Present'
end as status
from
from_thru
cross join generate_series (from_date, thru_date, interval '1 day') gs (date)
cross join users u
left join off_days o on
extract (dow from gs.date)::text = o.days and
u.user_id = o.userid
left join attendance_FHLHR a on
a.checktime::date = gs.date and
a.user_id = u.user_id
group by
gs.date, u.user_id, o.userid
答案 4 :(得分:0)
with from_thru as (
select
min (checktime)::date as from_date,
max (checktime)::date as thru_date
from attendance_FHLHR
),
users as (
select distinct userid, name, emp_id from attendance_FHLHR
)
select
gs.date::date, u.userid, u.name, to_char(gs.date::date, 'day') days,
min (a.checktime) as TimeIn,
max (a.checktime) as TimeOut,
extract (epoch from max (a.checktime) - min (a.checktime))/3600 as Hours,
case
when uso.off_days = 'monday' and min (a.checktime) is null then 'Off Day'
when uso.off_days = 'tuesday' and min (a.checktime) is null then 'Off Day'
when uso.off_days = 'wednesday' and min (a.checktime) is null then 'Off Day'
when max(a.checktime) is null then 'ABsent'
when count (1) = 1 then 'Half Day'
else 'Present'
end as status,
case when uso.off_days = 'monday' then 'monday'
when uso.off_days = 'tuesday' then 'tuesday'
when uso.off_days = 'wednesday' then 'wednesday'
end as ccc
from
from_thru
cross join generate_series (from_date, thru_date, interval '1 day') gs (date)
cross join users u
left join attendance_FHLHR a on
a.checktime::date = gs.date and
a.userid = u.userid
left join users_staffuser us on u.emp_id::varchar = us.emp_id
left join users_staffuseroffdays uso on uso.staff_user_id = us.id
-- and uso.off_days = to_char(gs.date::date, 'day')
and us.emp_id is not null
and uso.off_days = trim(to_char(gs.date::date, 'day'))
group by
gs.date, u.userid, u.name, uso.off_days
order by u.name ASC