Изменчивые выражения и подзапросы в 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,

Другие вопросы по тегам