Получение дней между двумя датами для нескольких идентификаторов - PullRequest
0 голосов
/ 04 июля 2018

Это дополнительный вопрос из Как получить список месяцев между 2 указанными датами, используя запрос? действительно. (Я подозреваю, что это потому, что я не совсем понимаю логику в предложениях connect by level!)

Что у меня есть список данных, как это

ID | START_DATE  | END_DATE
 1 | 01-JAN-2018 | 20-JAN-2018
 2 | 13-FEB-2018 | 20-MAR-2018
 3 | 01-MAR-2018 | 07-MAR-2018

и я хочу попробовать получить список со всеми днями между начальной и конечной датой для каждого идентификатора.

Так, например, я хочу список, который дает

ID | DATE
 1 | 01-JAN-2018
 1 | 02-JAN-2018
 1 | 03-JAN-2018 
...
 1 | 19-JAN-2018
 1 | 20_JAN-2018
 2 | 13-FEB-2018
 2 | 14-FEB-2018
 2 | 15-FEB-2018 
...

и т.д.

Я пытался адаптировать один из ответов по приведенной выше ссылке следующим образом

select id
, trunc(start_date+((level-1)),'DD') 
from (
  select id
  , start_date
  , end_date
  from blah
 ) 
connect by level <= ((trunc(end_date,'DD')-trunc(start_date,'DD'))) + 1

, который дает мне то, что я хочу, а затем целый ряд повторяющихся дат, как будто это декартово соединение. Есть что-то простое, что мне нужно добавить, чтобы это исправить?

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

Иерархический запрос, который вы пытались выполнить, должен включать id = prior id в предложении connect-by, но поскольку это вызывает циклы с несколькими строками источника, вам также необходимо включить вызов недетерминированной функции, такой как dbms_random.value * * 1003

select id, start_date + level - 1 as day
from blah
connect by level <= end_date - start_date + 1
and prior id = id
and prior dbms_random.value is not null

С вашими примерами данных в CTE это возвращает 63 строки назад:

with blah (ID, START_DATE, END_DATE) as (
            select 1, date '2018-01-01', date '2018-01-20' from dual
  union all select 2, date '2018-02-13', date '2018-03-20' from dual
  union all select 3, date '2018-03-01', date '2018-03-07' from dual
)
select id, start_date + level - 1 as day
from blah
connect by level <= end_date - start_date + 1
and prior id = id
and prior dbms_random.value is not null;

        ID DAY       
---------- ----------
         1 2018-01-01
         1 2018-01-02
         1 2018-01-03
...
         1 2018-01-19
         1 2018-01-20
         2 2018-02-13
         2 2018-02-14
...
         3 2018-03-05
         3 2018-03-06
         3 2018-03-07

Вам не нужно trunc() даты, если они не относятся к полуночному времени, что кажется маловероятным в этом случае, и даже тогда это может не потребоваться, если только у даты окончания есть более позднее время (например, 23 : 59: 59) * * +1010.

Рекурсивный CTE во многих отношениях более интуитивен, по крайней мере, когда вы поймете их основную идею; так что я бы, наверное, тоже использовал подход Гордона Могут быть различия в производительности и в том, работают ли они вообще для больших объемов данных (или сгенерированных строк), но для большого количества данных в любом случае стоит сравнить разные подходы, чтобы найти наиболее подходящий / производительный.

0 голосов
/ 04 июля 2018

Мне нравятся рекурсивные CTE:

with cte as (
      select id, start_dte as dte, end_dte
      from blah
      union all
      select id, dte + 1, end_dte
      from cte
      where dte < end_dte
     )
select *
from cte
order by id, dte;

Это стандартный синтаксис ANSI и работает в нескольких других базах данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...