Найти тему
Oracle APEX

Рекурсивный lag() в SQL запросе

Классический SQL не допускает межстрочного взаимодействия; аналитические функции это ограничение снимают; конструкция model дает еще большую свободу действий.

Возникла задача вида

$z = 1;
for ($i = 0; $i < 10; $i++) {
$z = $z * $z + 1;
}

Т.е. необходимо обращаться к предыдущему значению переменной z; возникло желание записать на SQL. "На языке"-то просто, но это 3-й уровень абстракции, а как на SQL, который является 4-м уровнем абстракции и специфицирует не действия, как 3-й, а желаемые результаты?

Вообще говоря, это вопрос философский. Сначала человек думает: "Я хочу примерно этого" - и махом формирует алгоритм, (c) Лев Толстой:

Die erste Kolonne marschiert, die zweite Kolonne marschiert...

"Первая колонна марширует, вторая колонна марширует...", а SQL-то требует совсем другого:

Ты скажи, ты скажи,
Чо те надо, чо надо,
Может, дам я тебе чо ты хошь...

Такова изначальная диспозиция: 3-й уровень определяет действия, 4-й уровень оперирует понятием желаемого результата.

А потом началось. Хотя нас и учили диалектике добре, я всегда путался в двух диалектических законах:

  1. Отрицание отрицания, и
  2. Единство и борьба противоположностей.

Но не суть.

Oracle в своем SQL постепенно допустил "программистские" вставки. Стало возможно строить SQL запросы с взаимодействием строк (аналитические функции), а потом и вовсе писать программные фрагменты в SQL запросе (конструкция model). Ересь супротив всех основ, конечно, но до чего же удобно! Не надо выпрыгивать в контекст процедурного PL/SQL, а, оставаясь в чистом SQL, работать со строками, как с элементами массива!!

Вот решение на SQL от Oracle описанной выше "на языке" задачи с внешним генератором данных:

with t as (select level as x from dual connect by level <= :s)
select z from t
model dimension by (x) measures (0 as z) rules (
z[x = 1] = 1,
z[x > 1] = z[cv() - 1] * z[cv() - 1] + 1
)
order by x
;

1
2
5
26
677

А вот решение с встроенным генератором данных:

select z from dual
model dimension by (1 as x) measures (0 as z) rules (
z[x = 1] = 1,
z[for x from 2 to :s increment 1] = z[cv() - 1] * z[cv() - 1] + 1
)
order by x
;

1
2
5
26
677

Не правда ли, изящно?!