【问题标题】:Gaps and islands for school vacations in a country with federal states在一个拥有联邦州的国家,学校假期的差距和岛屿
【发布时间】:2020-05-24 00:23:40
【问题描述】:

periods 表包含学生放学期间的数据。 locations 表包含国家和联邦州。我想列出从 '2019-12-15' 到 '2020-01-15' 的所有时间段,并累积每个岛屿的总休息天数。

要收集数据,我必须搜索位置 1 和位置 2 的所有时段。因为例如周末是与国家相关的时期,学校假期与联邦相关。

数据

所有数据都存储在 PostgreSQL 数据库中。我无法控制表结构。

位置

|----|-------------------|------------------|------------|
| id |       name        | is_federal_state | is_country |
|----|-------------------|------------------|------------|
|  1 | Deutschland       | f                | t          |
|  2 | Baden-Württemberg | t                | f          |
|----|-------------------|------------------|------------| 

“德国”是国家德国。 “巴登-符腾堡”是德国的一个联邦州。

周期

|-----|------------|------------|-------------|
| id  | starts_on  |  ends_on   | location_id |
|-----|------------|------------|-------------|
| 678 | 2019-12-21 | 2019-12-22 |           1 |
| 534 | 2019-12-23 | 2020-01-04 |           2 |
| 679 | 2019-12-28 | 2019-12-29 |           1 |
|   9 | 2020-01-01 | 2020-01-01 |           2 |
| 776 | 2020-01-04 | 2020-01-05 |           1 |
|   7 | 2020-01-06 | 2020-01-06 |           2 |
| 777 | 2020-01-11 | 2020-01-12 |           1 |
|-----|------------|------------|-------------|

periods 表存储有关学生不必上学的时期的信息。 678、679、776 和 777 是与国家相关的周末(id 1)。另一个是与联邦州相关的学校假期或公共假期(id 2)。

查询

Find rows with adjourning date ranges and accumulate their durations 的帮助下,我发现这是一个空白和孤岛问题。但我不明白如何正确使用它。这就是我所拥有的:

SELECT
  p.*,
  (
      Max(ends_on) OVER (PARTITION BY location_id) - Min(starts_on) OVER (PARTITION BY location_id) 
  )
  + 1 AS duration  
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,
              lag(ends_on) OVER (PARTITION BY location_id 
            ORDER BY
(starts_on)) AS prev_eo 
            FROM
              periods 
            WHERE
              location_id IN 
              (
                  1,
                  2
              )
              AND starts_on > '2019-12-15' 
              AND starts_on < '2020-01-15' 
        )
        p 
  )
  p;

结果

|-----|------------|------------|-------------|-----|----------|
| id  | starts_on  |  ends_on   | location_id | grp | duration |
|-----|------------|------------|-------------|-----|----------|
| 678 | 2019-12-21 | 2019-12-22 |           1 |   0 |       23 |
| 679 | 2019-12-28 | 2019-12-29 |           1 |   1 |       23 |
| 776 | 2020-01-04 | 2020-01-05 |           1 |   2 |       23 |
| 777 | 2020-01-11 | 2020-01-12 |           1 |   3 |       23 |
| 534 | 2019-12-23 | 2020-01-04 |           2 |   0 |       15 |
|   9 | 2020-01-01 | 2020-01-01 |           2 |   0 |       15 |
|   7 | 2020-01-06 | 2020-01-06 |           2 |   1 |       15 |
|-----|------------|------------|-------------|-----|----------|

这个结果的问题:

  • 此结果假定位置 ID 1 的所有周末都是一个大岛。但他们不是。除一个周末外,其他所有周末都属于圣诞度假岛。
  • ID 2 的实际圣诞假期应导致持续时间为 15,其中包括应属于圣诞岛的期间 id 678(加上该期间的其他周末)。

我想要的结果

|-----|------------|------------|-------------|----------|
| id  | starts_on  |  ends_on   | location_id | duration |
|-----|------------|------------|-------------|----------|
| 678 | 2019-12-21 | 2019-12-22 |           1 |       15 |
| 679 | 2019-12-28 | 2019-12-29 |           1 |       15 |
| 776 | 2020-01-04 | 2020-01-05 |           1 |       15 |
| 777 | 2020-01-11 | 2020-01-12 |           1 |        2 |
| 534 | 2019-12-23 | 2020-01-04 |           2 |       15 |
|   9 | 2020-01-01 | 2020-01-01 |           2 |       15 |
|   7 | 2020-01-06 | 2020-01-06 |           2 |       15 |
|-----|------------|------------|-------------|----------|

这个结果中会生成哪个查询?

游乐场

所有数据和查询:https://rextester.com/FEL57082

【问题讨论】:

  • 我不明白“17”是如何计算的。
  • 另一个可能引起麻烦的问题是日期的使用方式。正常情况下,23日至24日为1天;你用它来表示两个。同样,21 日至 22 日,加上 23 日至 24 日,通常不包括 22 日至 23 日。实际上,如果每个日期都隐式放置在午夜,则每个结束日期都需要添加一天,以便大多数日期数学按预期工作。
  • @GordonLinoff 我修好了号码。 id 为 534 的期间应为 15. 13 天,加上 678 期间的 2 天。

标签: sql postgresql gaps-and-islands


【解决方案1】:

代码中存在错误。 grp 已计算,但未使用。所以,我想你想要:

SELECT p.*,
       (Max(ends_on) OVER (PARTITION BY location_id, grp) - Min(starts_on) OVER (PARTITION BY location_id, grp) 
       ) + 1 AS duration 
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 p.*,
                   lag(ends_on) OVER (PARTITION BY location_id ORDER BY starts_on) AS prev_eo 
            FROM periods p
            WHERE location_id IN (1, 2) AND
                  starts_on > '2019-12-15' AND
                  starts_on < '2020-01-15' 
          ) p 
  ) p;

但是,这会在周末返回 2,而不是 17

Here 是您对此查询的 Rextester。

【讨论】:

  • 但是周期 534 的结果应该是 15 而不是 13。周期 678 会延后。
猜你喜欢
  • 2020-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多