【发布时间】: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