【问题标题】:Find rows with adjourning date ranges and accumulate their durations查找具有延期日期范围的行并累积其持续时间
【发布时间】:2020-02-07 10:37:25
【问题描述】:

我的 PostgreSQL 数据库存储学校假期、公共假期和周末日期,供父母计划假期。很多时候,学校假期会被周末或公共假期推迟。我想显示学校假期的非上学天数。这应该包括任何延期的周末或公共假期。

示例数据

位置

SELECT id, name, is_federal_state 
FROM locations 
WHERE is_federal_state = true;
| id | name              | is_federal_state |
|----|-------------------|------------------|
| 2  | Baden-Württemberg | true             |
| 3  | Bayern            | true             |

holiday_or_vacation_types

SELECT id, name FROM holiday_or_vacation_types;
| id | name                  |
|----|-----------------------|
| 1  | Herbst                |
| 8  | Wochenende            |

“Herbst”是德语的“秋天”,“Wochenende”是德语的“周末”。

周期

SELECT id, starts_on, ends_on, holiday_or_vacation_type_id 
FROM periods 
WHERE location_id = 2 
ORDER BY starts_on;
| id  | starts_on    | ends_on      | holiday_or_vacation_type_id |
|-----|--------------|--------------|-----------------------------|
| 670 | "2019-10-26" | "2019-10-27" | 8                           |
| 532 | "2019-10-28" | "2019-10-30" | 1                           |
| 533 | "2019-10-31" | "2019-10-31" | 1                           |
| 671 | "2019-11-02" | "2019-11-03" | 8                           |
| 672 | "2019-11-09" | "2019-11-10" | 8                           |
| 673 | "2019-11-16" | "2019-11-17" | 8                           |

任务

我想select all periods 其中location_id 等于2。我想计算每个时期的持续时间(以天为单位)。这可以通过这个 SQL 查询来完成:

SELECT id, starts_on, ends_on, 
       (ends_on - starts_on + 1) AS duration, 
       holiday_or_vacation_type_id 
FROM periods
| id  | starts_on    | ends_on      | duration | holiday_or_vacation_type_id |
|-----|--------------|--------------|----------|-----------------------------|
| 670 | "2019-10-26" | "2019-10-27" | 2        | 8                           |
| 532 | "2019-10-28" | "2019-10-30" | 3        | 1                           |
| 533 | "2019-10-31" | "2019-10-31" | 1        | 1                           |
| 671 | "2019-11-02" | "2019-11-03" | 2        | 8                           |
| 672 | "2019-11-09" | "2019-11-10" | 2        | 8                           |
| 673 | "2019-11-16" | "2019-11-17" | 2        | 8                           |

任何查看日历的人都会看到 ID 670(周末)、532(秋季假期)和 533(秋季假期)被延期。所以他们加起来有 6 天的假期。 到目前为止,我用一个计算这个的程序来做这个。但这需要相当多的资源(实际表包含大约 500,000 个项目)。

问题 1

哪个 SQL 查询会产生以下输出(添加了 real_duration 列)?用 SQL 也能做到吗?

| id  | starts_on    | ends_on      | duration | real_duration | holiday_or_vacation_type_id |
|-----|--------------|--------------|----------|---------------|-----------------------------|
| 670 | "2019-10-26" | "2019-10-27" | 2        | 6             | 8                           |
| 532 | "2019-10-28" | "2019-10-30" | 3        | 6             | 1                           |
| 533 | "2019-10-31" | "2019-10-31" | 1        | 6             | 1                           |
| 671 | "2019-11-02" | "2019-11-03" | 2        | 2             | 8                           |
| 672 | "2019-11-09" | "2019-11-10" | 2        | 2             | 8                           |
| 673 | "2019-11-16" | "2019-11-17" | 2        | 2             | 8                           |

问题 2

可以在part_of_range 字段中列出休会期吗?这将是结果。用 SQL 能做到吗?

| id  | starts_on    | ends_on      | duration | part_of_range | holiday_or_vacation_type_id |
|-----|--------------|--------------|----------|---------------|-----------------------------|
| 670 | "2019-10-26" | "2019-10-27" | 2        | 670,532,533   | 8                           |
| 532 | "2019-10-28" | "2019-10-30" | 3        | 670,532,533   | 1                           |
| 533 | "2019-10-31" | "2019-10-31" | 1        | 670,532,533   | 1                           |
| 671 | "2019-11-02" | "2019-11-03" | 2        |               | 8                           |
| 672 | "2019-11-09" | "2019-11-10" | 2        |               | 8                           |
| 673 | "2019-11-16" | "2019-11-17" | 2        |               | 8                           |

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    这是一个空白和孤岛问题。在这种情况下,您可以使用lag() 来查看岛屿的起点,然后查看累积总和。

    最后的操作是一些聚合(使用窗口函数):

    SELECT p.*, 
          (Max(ends_on) OVER (PARTITION BY location_id, grp) - Min(starts_on) OVER (PARTITION BY location_id, grp) ) + 1 AS duration,
          Array_agg(p.id) OVER (PARTITION BY location_id) 
    FROM (SELECT p.*, 
                 Count(*) FILTER (WHERE prev_eo < starts_on - INTERVAL '1 day') OVER (PARTITION BY location_id ORDER BY starts_on) AS grp
          FROM (SELECT id, starts_on, ends_on, location_id, holiday_or_vacation_type_id, 
                       lag(ends_on) OVER (PARTITION BY location_id ORDER BY (starts_on)) AS prev_eo
                FROM periods 
               ) p
         ) p;
    

    【讨论】:

      猜你喜欢
      • 2015-12-19
      • 2020-10-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-24
      • 2021-11-01
      • 1970-01-01
      • 2015-07-10
      • 2021-09-18
      相关资源
      最近更新 更多