WITH w AS  NOT MATERIALIZED (
    SELECT * 
    FROM very_very_big_table
)
SELECT * 
FROM w AS w1 
    JOIN w AS w2 
        ON w1.key = w2.ref
WHERE w2.key = 123;

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


Это войдет в PostgreSQL 12, и это big deal. Давайте рассмотрим, почему


Программисты любят CTE, потому что это позволяет существенно улучшить читаемость кода. Ну действительно, некоторые аналитические запросы могут работать с десятками таблиц и различными группировками и фильтрами. Писать всё это одним большим запросом — гарантированно получится что-то нечитаемое. Поэтому с помощью оператора WITH мы последовательно, небольшими подзапросами (которым задается человекочитаемое имя) описываем логику работы, а потом выдаем результат. Очень удобно.


Точнее, было бы очень удобно, если бы не одно но: текущий PostgreSQL выполняет эти подзапросы отдельно друг от друга, материализовывает их (записывает результат во временную таблицу). Это может привести к существенному замедлению по сравнению с одним большим нечитабельным монстром. Особенно, если CTE-подзапросы возвращают миллионы строк.


Впрочем, бывают ситуации, когда такое отдельное выполнение работает во благо: есть такой оптимизационный трюк, когда часть сложного запроса лучше выполнить отдельно, а постгрес самостоятельно этого не понимает. Тогда мы выносим эту часть в CTE-подзапрос.


В общем, ситуации бывают разные, именно поэтому в Postgres 12 был сделан комит, добавляющий ключевые слова MATERIALIZED и NOT MATERIALIZED, которые указывают соответственно материализовывать ли запрос или инлайнить.


Более того, изменилось дефолтное поведение. Теперь CTE-подзапрос по умолчанию будет инлайниться, если его результат используется один раз. В противном случае будет как раньше материализовываться.

Комментарии (8)


  1. 411
    18.02.2019 01:32

    Наконец-то, жаль пользоваться этим на текущей инфраструктуре не выйдет, Гугл ещё сто лет будет cloud sql обновлять.


  1. faustxp
    18.02.2019 07:40

    Почему бы это как хинт не сделать, как в оракле?


    1. erogov
      18.02.2019 09:33

      We are not interested in implementing hints in the exact ways they are commonly implemented on other databases.

      https://wiki.postgresql.org/wiki/OptimizerHintsDiscussion


    1. eefadeev
      18.02.2019 11:06

      Зачем делать «как хинт» то, что можно сделать (и, в итоге, сделано) элементом нормального синтаксиса?


      1. faustxp
        18.02.2019 12:43

        Все таки это указание оптимизатору.


        1. eefadeev
          18.02.2019 13:23

          С одной стороны — логично. С другой — в аналогичной ситуации с материализованными представлениями факт требования материализации — тоже, в известной степени, указание оптимизатору.


          1. batyrmastyr
            18.02.2019 14:03

            По материализованным представлениям можно строить индексы.


            1. eefadeev
              18.02.2019 14:33

              Строго говоря оптимизатор может делать индексы по любым промежуточным резалтсетам (и у вас, как правило, нет никакой возможности на это повлиять). На что это влияет?
              По сути, когда вы создаёте материализованное представление вы создаёте (и хотите создать) таблицу. Просто у неё такой синтаксис специальный :) То же самое получается и здесь. С той лишь разницей что таблица эта — временная.