Изменчивые выражения и подзапросы в PostgreSQL
В блоге Брюса Момджяна " Генерация случайных данных с помощью SQL" он использовал следующий код для генерации 5 случайных строк:
SELECT
(
SELECT string_agg(x, '')
FROM (
SELECT chr(ascii('a') + floor(random() * 26)::integer)
FROM generate_series(1, 40 + b * 0) as f(g)
) AS y(x)
) AS result
FROM generate_series(1,5) as a(b);
result
------------------------------------------
plwfwcgajxdygfissmxqsywcwiqptytjjppgrvgb
sjaypirhuoynnvqjdgywfsfphuvzqbbilbhakyhf
ngtabkjfqibwahlicgisijatliuwgbcuiwujgeox
mqtnyewalettounachwjjzdrvxbbbpzogscexyfi
dzcstpsvwpefohwkfxmhnlwteyybxejbdltwamsx
(5 rows)
Я задавался вопросом, почему 'b * 0' в строке 6 требуется. Когда я удалил его, результат изменился на 5 точно таких же строк, что означает, что Postgres кэшировал внешнее выражение выбора (результат)!
Я не мог найти, как работает кэширование выражений в Postgres. Согласно документации, функция random() помечена как VOLATILE, поэтому я ожидаю, что любое выражение зависит от нее, также будет изменчивым.
Как работает кэширование выражений в Postgres? Это где-нибудь задокументировано? Почему 'b * 0' отключил кеш, а random() - нет?
Обновить:
Чтобы изучить проблему, я переместил 'b * 0' внутрь вызова floor(), чтобы он находился на той же позиции / уровне, что и random():
...
SELECT chr(ascii('a') + floor(random() * 26 + b * 0)::integer)
FROM generate_series(1, 40) as s(f)
...
Результат все еще не кэшируется; разные строки.
Обновление: еще один пример, чтобы показать проблему
create sequence seq_test;
SELECT (SELECT nextval('seq_test')) FROM generate_series(1,5);
?column?
----------
1
1
1
1
1
(5 rows)
1 ответ
Что ж, random()
сам по себе является изменчивым, поэтому вы не получите строки с одинаковыми символами, повторяющимися до конца.
Если вы посмотрите на планы для запросов с и без b*0
ты увидишь:
С b*0
:
Function Scan on generate_series a (cost=0.00..37530.00 rows=1000 width=4)
SubPlan 1
-> Aggregate (cost=37.51..37.52 rows=1 width=32)
-> Function Scan on generate_series (cost=0.01..25.01 rows=1000 width=0)
Без b*0
:
Function Scan on generate_series a (cost=37.52..47.52 rows=1000 width=0)
InitPlan 1 (returns $0)
-> Aggregate (cost=37.50..37.51 rows=1 width=32)
-> Function Scan on generate_series (cost=0.00..25.00 rows=1000 width=0)
Если PostgreSQL определяет, что внутренняя агрегация не зависит от a
затем он оценивается один раз как InitPlan
и изменчивость или нет выражений внутри не имеет значения. Вводя зависимость подзапроса от a
то есть делая его коррелированным подзапросом, оценка должна быть переделана для каждой строки a
,