Представление Postgresql со многими распространенными табличными выражениями медленное - PullRequest
0 голосов
/ 08 июня 2018

Это огромное упрощение моего запроса, но по сути у меня есть ряд общих табличных выражений, которые строятся друг на друге и которые я хотел бы превратить в представление.Проблема в том, что он очень медленный, когда я пытаюсь использовать представление, но очень быстрый, когда я запускаю запрос.

CREATE VIEW user_view AS
WITH cte AS(
  SELECT first,middle,last FROM user
),
cte2 AS(
  SELECT *,first + middle AS first_middle FROM cte
),
cte3 AS(
  SELECT *,first_middle + last AS full_name FROM cte2
)
  SELECT * from cte3;

Быстрый запрос

WITH cte AS(
  SELECT first,middle,last FROM user WHERE user_id = 5
),
cte2 AS(
  SELECT *,first + middle AS first_middle FROM cte
),
cte3 AS(
  SELECT *,first_middle + last AS full_name FROM cte2
)
  SELECT * from cte3;

Медленный запрос с использованием представления

SELECT * from user_view WHERE user_id = 5

1 Ответ

0 голосов
/ 08 июня 2018

Postgres реализует то, что называется «оптимизационным ограждением» для CTE.Это означает, что Postgres материализует каждый CTE для последующей обработки.Одним приятным эффектом является то, что на CTE можно ссылаться несколько раз, но код выполняется только один раз.Недостатком является то, что такие удобства, как указатели, «забываются» после того, как CTE материализован.

По вашему вопросу, представление на самом деле несущественно (без каламбура).В этой версии:

WITH cte AS (
      SELECT first, middle, last FROM user WHERE user_id = 5
     ),
     cte2 AS (
      SELECT *, first || middle AS first_middle FROM cte
     ),
     cte3 AS (
      SELECT *, first_middle || last AS full_name FROM cte2
    )
SELECT * 
FROM cte3;

Первый CTE предположительно вытаскивает одну запись из таблицы.Предположительно, он использует индекс по идентификатору, и даже эта операция выполняется очень быстро.Эта одна запись является единственной записью, обработанной оставшимися CTE.

В этой версии:

WITH cte AS (
      SELECT first, middle, last FROM user 
     ),
     cte2 AS (
      SELECT *, first || middle AS first_middle FROM cte
     ),
     cte3 AS (
      SELECT *, first_middle || last AS full_name FROM cte2
    )
SELECT * 
FROM cte3
WHERE user_id = 5;

CTE обрабатывают все данные в userТаблица.В конце должна быть найдена строка, соответствующая условию WHERE.Материализованный CTE больше не имеет индекса.,,поэтому данные ищутся последовательно.

Это поведение не применимо к подзапросам, поэтому вы можете попробовать переписать логику, используя подзапросы, а не CTE.

Postgres оптимизирует CTE по-разномуиз других баз данных.Например, SQL Server никогда не материализует подзапросы;код всегда «вставляется» в запрос и оптимизируется в целом.На самом деле форумы по SQL Server имеют противоположную задачу - реализовать возможность материализации CTE.отличается от других баз данных.Oracle - это одна база данных, которая, похоже, использует оба подхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...