我有一个包含以下列的表格:
personnel_id INT,日期DATE,时间TIME,但无时区。
每一个人每天都有进场和出场的机会。也可以有多个输入和输出。例如,某人可能在8:00出发并在13:00退出,然后在16:36再次返回并报到,最后在19:20退出。
因此,对于每个日期,我需要计算一个人上班的总时数,并据此计算出该人每个月的工作时间。因此,我需要一个选择来获取一个person_id并返回该人每个月的工作时间。例如:
ID 1 2 3 4 5 6 7 8 9 10 11 12
3 173.24 134 147.26 180 50.47 138 196.36 47 93.56 .56 78 139
答案 0 :(得分:1)
首先,您需要计算每个切入/切出组合的持续时间。
假设每个人员ID的每个日期的条目数始终为偶数,则可以使用以下内容计算每对的持续时间:
select personnel_id,
"date",
case
when row_number() over w % 2 = 0 then "time" - lag("time") over w
end as duration
from person_work
window w as (partition by personnel_id, "date" order by "time")
row_number()
是window function,为每一行分配一个数字。 lag()
是另一个窗口函数,它从上一行获取一列的值。由于这两个函数共享相同的“窗口定义”,因此我只在结尾处使用window
子句声明了一次。 CASE
表达式为第二行计算time
列的差。时钟输入行的行号为奇数,时钟输出行的行号为偶数。 % 2
检查偶数行号。
在下一步中,我们需要将成对的货币汇总为每月的持续时间。 这可以通过在先前的查询基础上完成。我正在使用common table expression重用上一个查询:
with hours as (
select personnel_id,
"date",
case
when row_number() over w % 2 = 0 then
-- this converts the interval into a decimal value
extract(epoch from "time" - lag("time") over w)/3600
end as hours
from person_work
window w as (partition by personnel_id, "date" order by "time")
), hours_per_month as (
select personnel_id,
extract(year from "date")::int as work_year,
extract(month from "date")::int as work_month,
sum(hours) work_hours
from hours
where hours is not null
group by personnel_id, work_year, work_month
)
select *
from hours_per_month;
extract(year from ...)
返回date
列的年份作为十进制值。 ::int
是type cast,将其简单地转换为整数。严格来说,这并不是必须的。
extract(epoch from ..)
以秒为单位返回interval的持续时间。将该结果除以3600将返回间隔,以小时为单位。
这将返回类似:
personnel_id | work_year | work_month | work_hours
-------------+-----------+------------+-----------
1 | 2018 | 1 | 25.33
1 | 2018 | 2 | 17.08
1 | 2018 | 3 | 8.25
然后在最后一步中,我们需要将行变成列。这可以通过使用filter子句进行条件聚合来完成:
with hours as (
select personnel_id,
"date",
case
when row_number() over w % 2 = 0 then extract(epoch from "time" - lag("time") over w)/3600
end as hours
from person_work
window w as (partition by personnel_id, "date" order by "time")
), hours_per_month as (
select personnel_id,
extract(year from "date")::int as work_year,
extract(month from "date")::int as work_month,
sum(hours) hours
from hours
where hours is not null
group by personnel_id, work_year, work_month
)
select personnel_id,
work_year,
sum(hours) filter (where work_month = 1) as hours_jan,
sum(hours) filter (where work_month = 2) as hours_feb,
sum(hours) filter (where work_month = 3) as hours_mar,
sum(hours) filter (where work_month = 4) as hours_apr,
sum(hours) filter (where work_month = 5) as hours_may,
sum(hours) filter (where work_month = 6) as hours_jun,
sum(hours) filter (where work_month = 7) as hours_Jul,
sum(hours) filter (where work_month = 8) as hours_aug,
sum(hours) filter (where work_month = 9) as hours_sep,
sum(hours) filter (where work_month = 10) as hours_oct,
sum(hours) filter (where work_month = 11) as hours_nov,
sum(hours) filter (where work_month = 12) as hours_dec
from hours_per_month
group by personnel_id, work_year;
这将返回如下内容:
personnel_id | work_year | hours_jan | hours_feb | hours_mar | hours_apr | hours_may | hours_jun | hours_jul | hours_aug | hours_sep | hours_oct | hours_nov | hours_dec
-------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+----------
1 | 2018 | 25.33 | 17.08 | 8.25 | ... | ... | ... | ... | ... | .... | .... | ... | ....
如果您只想要一份年度报告,则可以在最终选择中使用where work_year = ...
,然后从选择列表和group by