Запрос Oracle - выберите дату и время - перекрытие - PullRequest
1 голос
/ 18 октября 2019

устный запрос Дата и время с перекрытием

ID    startdate                enddate                      hours
a124     10/10/2019 07:30:00    10/10/2019 11:30:00            4
a124     10/10/2019 07:00:00    10/10/2019 15:10:00            8.17
bc24     10/10/2019 07:30:00    10/10/2019 11:30:00            4
bc24     10/10/2019 10:30:00    10/10/2019 15:30:00            5
er67     10/10/2019 09:30:00    10/10/2019 11:30:00            2
er67     10/10/2019 15:30:00    10/10/2019 16:30:00            1

Желаемый результат:

    ID    startdate                enddate                      hours
   a124   10/10/2019 07:00:00     10/10/2019 15:10:00           8.17
   bc24   10/10/2019 07:30:00     10/10/2019 15:30:00            8
   er67   10/10/2019 09:30:00     10/10/2019 11:30:00            2
   er67   10/10/2019 15:30:00     10/10/2019 16:30:00            1

Ответы [ 2 ]

2 голосов
/ 18 октября 2019

Я бы подошел к этому, используя lag(), совокупный sum() и агрегацию. Вот пошаговое объяснение.

Сначала вы можете использовать lag(), чтобы восстановить предыдущую дату начала и окончания для того же идентификатора:

select 
    t.*,
    lag(startdate) over(partition by id order by startdate) lagstartdate,
    lag(enddate)   over(partition by id order by startdate) lagenddate
from mytable t
ID   | STARTDATE           | ENDDATE             | HOURS | LAGSTARTDATE        | LAGENDDATE         
:--- | :------------------ | :------------------ | ----: | :------------------ | :------------------
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 |  8.17 | <em>null</em>                | <em>null</em>               
a124 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 |     4 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00
bc24 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 |     4 | <em>null</em>                | <em>null</em>               
bc24 | 2019-10-10 10:30:00 | 2019-10-10 15:30:00 |     5 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 |     2 | <em>null</em>                | <em>null</em>               
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 |     1 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00

Затем вы можете настроить совокупный sum для разрезания записей, имеющих одинаковые id внутри групп (которые впоследствии будут агрегированы). Когда даты не перекрываются, начинается новая группа:

select 
    t.*,
    sum(
        case when startdate <= lagenddate or enddate <= lagstartdate
            then 0
            else 1 
        end
    ) over(partition by id order by startdate) grp
from (
    select 
        t.*,
        lag(startdate) over(partition by id order by startdate) lagstartdate,
        lag(enddate)   over(partition by id order by startdate) lagenddate
    from mytable t
) t
ID   | STARTDATE           | ENDDATE             | HOURS | LAGSTARTDATE        | LAGENDDATE          | GRP
:--- | :------------------ | :------------------ | ----: | :------------------ | :------------------ | --:
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 |  8.17 | <em>null</em>                | <em>null</em>                |   1
a124 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 |     4 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 |   1
bc24 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 |     4 | <em>null</em>                | <em>null</em>                |   1
bc24 | 2019-10-10 10:30:00 | 2019-10-10 15:30:00 |     5 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 |   1
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 |     2 | <em>null</em>                | <em>null</em>                |   1
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 |     1 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 |   2

Наконец, вы можете сгруппировать записи по id и grp: min() и max() даст вам диапазон дат, затем вы сможете вычислить разницу дат.

Окончательный запрос:

select 
    id, 
    min(startdate) startdate,
    max(enddate) enddate,
    round((max(enddate) - min(startdate)) * 24, 2) hours
from (
    select 
        t.*,
        sum(
            case when startdate <= lagenddate or enddate <= lagstartdate
                then 0
                else 1 
            end
        ) over(partition by id order by startdate) grp
    from (
        select 
            t.*,
            lag(startdate) over(partition by id order by startdate) lagstartdate,
            lag(enddate)   over(partition by id order by startdate) lagenddate
        from mytable t
    ) t
) t
group by id, grp
order by id, grp
ID   | STARTDATE           | ENDDATE             | HOURS
:--- | :------------------ | :------------------ | ----:
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 |  8.17
bc24 | 2019-10-10 07:30:00 | 2019-10-10 15:30:00 |     8
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 |     2
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 |     1

Демонстрация на DB Fiddle

0 голосов
/ 18 октября 2019

Вы можете использовать следующие аналитические функции (LAG и SUM) для создания групп дат без пробелов:

SQL> SELECT
  2      ID,
  3      MIN(STARTDATE),
  4      MAX(ENDDATE),
  5      ROUND(SUM(CASE
  6          WHEN PREV_ENDDATE BETWEEN STARTDATE AND ENDDATE THEN ENDDATE - PREV_ENDDATE
  7          ELSE ENDDATE - STARTDATE
  8      END) * 24, 2) AS HOURS
  9  FROM
 10      (
 11          SELECT
 12              T.*,
 13              SUM(CASE
 14                  WHEN T.PREV_ENDDATE < T.STARTDATE THEN 1
 15              END) OVER(
 16                  PARTITION BY ID
 17                  ORDER BY
 18                      STARTDATE, ENDDATE
 19              ) SM
 20          FROM
 21              (
 22                  SELECT
 23                      ID,
 24                      STARTDATE,
 25                      ENDDATE,
 26                      LAG(ENDDATE) OVER(
 27                          PARTITION BY ID
 28                          ORDER BY
 29                              STARTDATE, ENDDATE
 30                      ) AS PREV_ENDDATE
 31                  FROM
 32                      T TOUT
 33                  WHERE
 34                      NOT EXISTS (
 35                          SELECT
 36                              1
 37                          FROM
 38                              T TIN
 39                          WHERE
 40                              TIN.ID = TOUT.ID
 41                              AND TOUT.STARTDATE BETWEEN TIN.STARTDATE AND TIN.ENDDATE
 42                              AND TOUT.ENDDATE BETWEEN TIN.STARTDATE AND TIN.ENDDATE
 43                              AND TOUT.ROWID <> TIN.ROWID
 44                      )
 45              ) T
 46      )
 47  GROUP BY
 48      ID,
 49      SM;

ID   MIN(START MAX(ENDDA      HOURS
---- --------- --------- ----------
a124 10-OCT-19 10-OCT-19       8.17
bc24 10-OCT-19 10-OCT-19          8
er67 10-OCT-19 10-OCT-19          1
er67 10-OCT-19 10-OCT-19          2

SQL>

Cheers !!

...