具有许多常见表表达式的 Postgresql 视图很慢

use*_*182 1 sql postgresql

这是对我的查询的极大简化,但基本上我有一系列公共表表达式,它们相互构建,我想将它们转换为视图。问题是当我尝试使用视图时它非常慢,但当我运行查询时非常快。

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;
Run Code Online (Sandbox Code Playgroud)

快速查询

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;
Run Code Online (Sandbox Code Playgroud)

使用视图慢查询

SELECT * from user_view WHERE user_id = 5
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 5

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;
Run Code Online (Sandbox Code Playgroud)

第一个 CTE 大概从表中拉出一条记录。据推测,它在 id 上使用索引,甚至该操作也非常快。该记录是由其余 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;
Run Code Online (Sandbox Code Playgroud)

CTE 正在处理表中的所有数据user。最后,WHERE需要找到满足条件的行。物化的 CTE 不再有索引。. . 所以数据是按顺序搜索的。

此行为不会适用于子查询,所以你可以尝试重写使用子查询,而不是热膨胀系数你的逻辑。

Postgres 优化 CTE 的方式与其他数据库不同。例如,SQL Server从不具体化子查询;代码总是“插入”到查询中并作为一个整体进行优化。事实上,SQL Server 论坛有相反的关注——实现一个选项来实现 CTE。与其他数据库不同。Oracle 是一种似乎同时采用两种方法的数据库。