#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;
С точки зрения производительности этот запрос может быть не лучшим подходом, поскольку существует так много самосоединений, но также существует довольно много бизнес-правил.
Ответ №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 в соответствии со столбцом результатов в моем примере.