Таблица periods
содержит данные периодов, когда школа не работает для учащихся. Таблица locations
содержит страны и федеральные земли. Я хочу перечислить все периоды от '2019-12-15' до '2020-01-15' и накапливать общее количество выходных дней для каждого острова.
Для сбора данных мне нужно искать для всех периодов местоположения 1 и местоположения 2. Потому что, например, выходные дни являются периодами, которые связаны со страной, а школьные каникулы связаны с федеральным государством.
Данные
Все данные хранится в базе данных PostgreSQL. Я не контролирую структуру таблицы.
location
|----|-------------------|------------------|------------|
| 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). Другие - школьные каникулы или публичные c праздники, которые связаны с федеральным государством (id 2).
Запрос
С помощью Поиск строк с диапазонами дат закрытия. и накапливать их продолжительность Я обнаружил, что это проблема пробелов и островов. Но я не понимаю, как я могу использовать это правильно. Вот что у меня есть:
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 |
|-----|------------|------------|-------------|-----|----------|
Проблемы с этим результатом:
- Этот результат предполагает, что все выходные с идентификатором местоположения 1 один большой остров. Но это не так. Все выходные, кроме одного, принадлежат острову рождественских каникул.
- Фактические рождественские каникулы с идентификатором 2 должны иметь продолжительность 15, включая период 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 |
|-----|------------|------------|-------------|----------|
Какой запрос будет сгенерирован в этом результате?
Playground
Все данные и запрос: https://rextester.com/FEL57082