从离开餐桌的天数计算每月的天数
Posted
技术标签:
【中文标题】从离开餐桌的天数计算每月的天数【英文标题】:Count days per month from days off table 【发布时间】:2021-01-28 07:54:26 【问题描述】:我有存储人、假期开始和假期结束的表格。
我需要从中计算,一个人每个月有多少个工作日休假。所以我想在一个月内对这张表进行分区。 为了获得假期,我正在使用:https://github.com/christopherthompson81/pgsql_holidays
假设我只有一个人的桌子,只有开始/停止。
create table data (id int, start date, stop date);
这是我写的 network_days 的函数:
CREATE OR REPLACE FUNCTION network_days(start_date date , stop_date date) RETURNS bigint AS $$
SELECT count(*) FROM
generate_series(start_date , stop_date - interval '1 minute' , interval '1 day') the_day
WHERE
extract('ISODOW' FROM the_day) < 6 AND the_day NOT IN (
SELECT datestamp::timestamptz FROM holidays_poland (extract(year FROM o.start_date)::int, extract(year FROM o.stop_date)::int))
$$
LANGUAGE sql
STABLE;
我创建了带有如下查询的函数:
--$2 = 2020
SELECT
month, year, sum(value_per_day)
FROM (
SELECT to_char(dt , 'mm') AS month, to_char(dt, 'yyyy') AS year, (network_days ((
CASE WHEN EXTRACT(year FROM df.start_date) < 2020 THEN (SELECT date_trunc('year' , df.start_date) + interval '1 year')::date
ELSE df.start_date END) , ( CASE WHEN EXTRACT(year FROM df.stop_date) > $2 THEN (date_trunc('year' , df.stop_date))::date
ELSE
df.stop_date END))::int ::numeric / count(*) OVER (PARTITION BY id))::int AS value_per_day
FROM intranet.dayoff df
LEFT JOIN generate_series((
CASE WHEN EXTRACT(year FROM df.start_date) < $2 THEN (SELECT date_trunc('year' , df.start_date) + interval '1 year')::date ELSE df.start_date
END) , (CASE WHEN EXTRACT(year FROM df.stop_date) > $2 THEN (date_trunc('year' , df.stop_date))::date
ELSE df.stop_date END) - interval '1 day' , interval '1 day') AS t (dt) ON extract('ISODOW' FROM dt) < 6
WHERE
extract(isodow FROM dt) < 6 AND (EXTRACT(year FROM start_date) = $2 OR EXTRACT(year FROM stop_date) = $2)) t
GROUP BY month, year
ORDER BY month;
基于:https://dba.stackexchange.com/questions/237745/postgresql-split-date-range-by-business-days-then-aggregate-by-month?rq=1
我几乎拥有它: 返回 10 行
| month | year | sum |
| ----- | ---- | ---- |
| 03 | 2020 | 2 |
| 04 | 2020 | 13 |
| 06 | 2020 | 1 |
| 11 | 2020 | 1 |
| 12 | 2020 | 2 |
| 05 | 2020 | 1 |
| 10 | 2020 | 2 |
| 08 | 2020 | 10 |
| 01 | 2020 | 1 |
| 02 | 2020 | 1 |
所以在我创建的函数中,我需要添加类似这样的内容
dt NOT IN (SELECT datestamp::timestamptz FROM holidays_poland ($2, $2))
但我最终会遇到很多情况,我觉得这种方法是错误的。 我觉得我应该以某种方式将表格从:
id start stop
1 31.12.2019 00:00:00 01.01.2020 00:00:00
2 30.03.2020 00:00:00 14.04.2020 00:00:00
3 01.05.2020 00:00:00 03.05.2020 00:00:00
到
start stop
30.03.2020 00:00:00 01.01.2020 00:00:00
01.01.2020 00:00:00 14.04.2020 00:00:00
01.05.2020 00:00:00 03.05.2020 00:00:00
并且只是针对这个日期范围运行 network_days 函数,但我无法成功地对表的查询进行分区以获得这样的结果。 您认为实现我想要计算的最佳方法是什么?
【问题讨论】:
请详细说明您想要实现的目标。我不清楚您要计算什么。此外:据我所知,您最后一个数据集不适合输入。为什么三月开始,一月停止? 周末怎么样?周末是否只包含在假期表中? 我想计算两个日期之间每月的工作日,不包括节假日和周末。 看我的回答,相信可以解决问题 【参考方案1】:demo:db<>fiddle
SELECT
gs::date
FROM person_holidays p,
generate_series(p.start, p.stop, interval '1 day') gs -- 1
WHERE gs::date NOT IN (SELECT holiday FROM holidays) -- 2
AND EXTRACT(isodow from gs::date) < 6 -- 3
-
根据人的开始和停止日期生成日期系列
从假期表中排除所有日期
如有必要:排除所有周末(周六和周日)
之后你就可以GROUP BY
个月并统计记录:
SELECT
date_trunc('month', gs),
COUNT(*)
FROM person_holidays p,
generate_series(p.start, p.stop, interval '1 day') gs
WHERE gs::date NOT IN (SELECT holiday FROM holidays)
and extract(isodow from gs::date) < 6
GROUP BY 1
【讨论】:
以上是关于从离开餐桌的天数计算每月的天数的主要内容,如果未能解决你的问题,请参考以下文章