Рекурсивная сумма в красном смещении

#sql #postgresql #amazon-redshift

#sql #postgresql #amazon-redshift

Вопрос:

 CREATE TABLE testit (
  id INT, v1 INT, v2 INT, result INT);

INSERT
   INTO testit (id, v1, v2, result)
   VALUES 
     (1, 1,     2, 1 )
   , (2, 4,     3, 4 )
   , (3, 6,     7, 6 )
   , (4, NULL, 10, 13)
   , (5, NULL, 12, 25)
;
 

Учитывая первые три столбца id, v1, v2, я хочу написать запрос, который возвращает столбец «результат»:

  • v1, если v1 не равно нулю
  • (рекурсивная) сумма предыдущих строк v1 и v2 ig v1 равна нулю (или, альтернативно: последнее значение v1 и сумма v2 между первой строкой, где v1 равно нулю, и предыдущей строкой)

Возможно ли это? Ссылка на SQLFiddle

Комментарии:

1. Как вы вычислили результат как 13 для id = 4?

2. Это было бы просто с помощью оконных функций или рекурсивных CTE, но ParAccel (Redshift) не имеет ни того, ни другого, AFAIK.

3. @JosephB 6 7 приведенная выше строка v1 v2.

4. @Roberto Спасибо за разъяснение. Пожалуйста, смотрите мой ответ ниже.

Ответ №1:

Следующий запрос дает вам желаемый результат. 3 разных запроса возвращают следующие результирующие наборы, объединенные UNION ALL :

если v1 текущей строки не равно null

если v1 текущей строки равно нулю, а v1 предыдущей строки не равно нулю

если v1 текущей строки равно нулю, а v1 предыдущей строки равно нулю

 select t_main.id, t_main.v1, t_main.v2, results.result
from 
testit t_main
inner join
(
  select id, result
  from testit
  where v1 is not null
  union all
  select t1.id, max(t2.v1 t2.v2) sum_result
  from testit t1 
  inner join testit t2 on t2.id = t1.id-1 and t2.v1 is not null
  where t1.v1 is null
  group by t1.id
  union all
  select
    to1.id, max(to3.v1 to3.v2 to1.v2)
  from testit to1
  inner join testit to2 on to2.id = to1.id-1 and to2.v1 is null 
  inner join 
  (
    select t1.id t1_id, max(t3.id) t3_id
    from testit t1 
    inner join testit t2 on t2.id = t1.id-1 and t2.v1 is null
    inner join testit t3 on t3.id < t1.id and t3.v1 is not null
    where t1.v1 is null
    group by t1.id
  ) max_id on to1.id = max_id.t1_id
  inner join testit to3 on max_id.t3_id = to3.id
  group by to1.id
) results
on t_main.id = results.id
order by t_main.id;
 

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

SQL Fiddle

Ответ №2:

Выражение SQL является:

 select ti.*,
       sum(coalesce(v1, c2)) over (order by id)
from testit ti;
 

Я не уверен на 100%, что Redshift поддерживает кумулятивную сумму без range rows опции or. Так что это может быть либо:

 select ti.*,
       sum(coalesce(v1, c2)) over (order by id range between unbounded preceding and current row)
from testit ti;
 

или:

 select ti.*,
       sum(coalesce(v1, c2)) over (order by id rows between unbounded preceding and current row)
from testit ti;
 

Извинения . , , у меня сейчас нет доступа к RedShift. Иногда он привередлив в отношении синтаксиса, который он принимает для функций Windows. Но один из этих трех должен работать.

Комментарии:

1. то, что вы говорите, не работает: sqlfiddle.com /#!11/c5007/49 Он суммирует все v1 с текущей строкой c2, тогда как я хочу только предыдущий v1 с предыдущим v2 в соответствии со столбцом результатов в моем примере.