Предложение SQL Connect - генерировать все данные по датам

Данные в таблице хранятся по дате вступления в силу. Не могли бы вы помочь мне с оператором ORACLE SQL, который реплицирует данные 8/1 на 8/2, 8/3,8/4 и повторяет значение 8/5 после?

DATE             VALUE1    VALUE2    
8/1/2017           x         1  
8/1/2017           x         2   
8/7/2017           y         4  
8/7/2017           x         3  

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

DATE             VALUE1     VALUE2  
8/1/2017           x          1  
8/1/2017           x          2  
8/2/2017           x          1  
8/2/2017           x          2  

... повторять до 8/6

8/7/2017           y         4  
8/7/2017           x         3  
8/8/2017           y         4  
8/8/2017           x         3  

... повторить для sysdate - 1


person user3498646    schedule 21.08.2017    source источник
comment
Если вы знаете, что вам нужно предложение connect-by, как далеко вы продвинулись с запросом и какие проблемы у вас возникли?   -  person Alex Poole    schedule 21.08.2017
comment
Я думаю, что он может просто использовать соединение для создания произвольных строк, а не иерархически упорядочивать свои данные.   -  person Caius Jard    schedule 21.08.2017
comment
Кстати, это не дубликат stackoverflow.com/questions/45743422/ ? Все в этом вопросе похоже на тот другой ..   -  person Caius Jard    schedule 21.08.2017
comment
@CaiusJard - если вы прочитаете мои комментарии к ответу в этой теме, вы увидите, что это на самом деле не дубликат. Наоборот, это другой вопрос (хотя он может показаться похожим и из того же постера), поэтому я попросил его/ее задать его в отдельной (новой) ветке.   -  person mathguy    schedule 22.08.2017


Ответы (2)


Вот один из способов сделать это. Это не самый элегантный или эффективный способ, но это самый элементарный способ, который я мог придумать (за исключением действительно неэффективных вещей, таких как коррелированные подзапросы, которые нельзя легко раскрутить до соединений).

В первом подзапросе с псевдонимами a я создаю все необходимые даты. Во втором подзапросе, b, я создаю диапазоны дат, для которых нам нужно будет повторять определенные строки (в тестовых данных я допускаю переменное количество строк, которые должны повторяться, чтобы сделать одну из тонкостей проблема более очевидна).

Имея их в руках, легко получить результат, объединив эти два подзапроса и исходные данные. Увы, этот подход требует трехкратного чтения базовой таблицы; надеюсь, у вас не слишком много данных для обработки.

with
     inputs ( dt, val1, val2 ) as (
       select date '2017-08-14', 'x', 1 from dual union all
       select date '2017-08-14', 'x', 2 from dual union all
       select date '2017-08-17', 'y', 4 from dual union all
       select date '2017-08-17', 'x', 3 from dual union all
       select date '2017-08-19', 'a', 5 from dual
     )
-- End of simulated inputs (for testing purposes only, not part of the solution).
-- Use your actual table and column names in the SQL query below.
select a.dt, i.val1, i.val2
from   (
         select min_dt + level - 1 as dt
         from   ( select min(dt) as min_dt from inputs )
         connect by level <= sysdate - min_dt
       ) a
       join
       (
         select dt, lead(dt, 1, sysdate) over (order by dt) as lead_dt
         from   (select distinct dt from inputs)
       ) b
         on a.dt >= b.dt and a.dt < b.lead_dt
       join
       inputs i on i.dt = b.dt
order by dt, val1, val2
;

Вывод:

DT         VAL1 VAL2
---------- ---- ----
2017-08-14 x       1
2017-08-14 x       2
2017-08-15 x       1
2017-08-15 x       2
2017-08-16 x       1
2017-08-16 x       2
2017-08-17 x       3
2017-08-17 y       4
2017-08-18 x       3
2017-08-18 y       4
2017-08-19 a       5
2017-08-20 a       5
person mathguy    schedule 21.08.2017

Вы хотите использовать аналитическую функцию LAST_VALUE, например:

 select 
   fakedate,
   CASE 
     WHEN flip=1 THEN 
       LAST_VALUE(yourvalue1rown1 IGNORE NULLS) OVER(ORDER BY fakedate) 
     ELSE
       LAST_VALUE(yourvalue1rown2 IGNORE NULLS) OVER(ORDER BY fakedate) 
   END as lastvalue1,
   CASE 
     WHEN flip=1 THEN 
       LAST_VALUE(yourvalue2rown1 IGNORE NULLS) OVER(ORDER BY fakedate) 
     ELSE
       LAST_VALUE(yourvalue2rown2 IGNORE NULLS) OVER(ORDER BY fakedate) 
   END as lastvalue2    
 from

 select
   fakedate, flip,
   CASE WHEN rown = 1 THEN yourvalue1 END as yourvalue1rown1,
   CASE WHEN rown = 2 THEN yourvalue1 END as yourvalue1rown2,
   CASE WHEN rown = 1 THEN yourvalue2 END as yourvalue2rown1,
   CASE WHEN rown = 2 THEN yourvalue2 END as yourvalue2rown2
 from
   (select (sysdate - 100) + trunc(rownum/2) fakedate, mod(rownum, 2)+1 as flip from dual connect by level <= 100) fakedates
   left outer join
   (select yt.*, row_number() over(partition by yourdate order by yourvalue1) as rown) yourtable
   on
     fakedate = yourdate and flip = rown 

Вам нужно будет настроить имена столбцов, чтобы они соответствовали вашей таблице. Вам также придется настроить 100, чтобы отразить, сколько дней назад вам нужно перейти, чтобы перейти к началу ваших данных о дате.

Обратите внимание, что это не проверено (у SQLFiddle есть некоторые проблемы с оракулом для меня в данный момент), поэтому, если вы получите какие-либо синтаксические ошибки или другие незначительные вещи, которые вы не можете исправить, прокомментируйте, и я их исправлю.

person Caius Jard    schedule 21.08.2017
comment
Вероятно, это не сработает; есть ДВЕ строки для 8/1, и значения в двух столбцах должны быть реплицированы ОБЕ для 8/2 и снова для 8/3 и т. д. - person mathguy; 22.08.2017
comment
Хороший вопрос. Теперь мне любопытно узнать, работает ли отредактированная версия, но sqlfiddle все еще не работает с оракулом. - person Caius Jard; 22.08.2017