Пробелы и острова для школьных каникул в стране с федеральными землями - PullRequest
0 голосов
/ 07 февраля 2020

Таблица 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

1 Ответ

1 голос
/ 07 февраля 2020

В коде есть ошибка. 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.

Здесь ваш Rextester с этим запрос.

...