从离开餐桌的天数计算每月的天数

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

【讨论】:

以上是关于从离开餐桌的天数计算每月的天数的主要内容,如果未能解决你的问题,请参考以下文章

计算每月的业务/活跃天数

如何用excel计算月份和天数?

从日期范围计算特定月份的天数。谷歌表格

excel中怎样自动计算当月天数?

利用Python写一个闰年计算器和每月天数计算器

如何用PHP+MySQL计算两个日期之间的月份数和其余下的天数?