CTE真的是一个观点吗?

时间:2017-04-25 18:49:10

标签: sql postgresql common-table-expression

我一直认为CTE应该被视为内联视图宏。所以我的想法是:如果未引用/使用CTE,则不会执行。这只是一个定义,仅此而已。

但是,请采取以下查询:

create table t
(
    id int primary key
);

with
a as
(
    insert into t(id) values(1)
)
select false;

select * from t;

似乎在基于CTE的查询之后,select * from t返回插入CTE中的元组。尽管没有使用CTE,为什么要插入这个元组?

这是设计还是规格?依赖这种行为是否安全?这允许执行多个查询,这些查询在一个查询中完全不相关。

这似乎与以下信息相矛盾:https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/#comment-19121

2 个答案:

答案 0 :(得分:4)

  • 语法 CTE的行为与任何其他表表达式相同。

  • 在语义上它是不同的。 [在Postgres中] 始终执行一次,即使它被多次引用也是如此。

  • [在Postgres] CTE将作为优化障碍;查询术语不能在CTE和主查询之间移动(进入或退出)。

第二点和第三点可能会产生严重影响。由于屏障和恰好一次,CTE扫描很难利用隐式顺序或CTE内部的索引的存在。 CTE扫描或多或少的行为类似于无序表或物化视图上的顺序扫描。对于小型CTE,这将没有问题,因为可以使用散列连接。大型CTE需要实现+排序才能将CTE加入主查询。

答案 1 :(得分:0)

在postgres中,cte 不应该被视为内联视图,尽管将其视为存在于语句范围内的物化视图是有用的。如果在查询的另一部分中引用CTE,或者如果它更改数据(INSERT / UPDATE / DELETE),则将实现CTE。

因此,由于您的示例会更改数据,因此会对CTE进行评估,而您引用的链接的CTE不会更改数据。

而在其他数据库中,外部查询的谓词将由优化器推送到CTE,在postgresql中,CTE将完全实现。

e.g。

WITH cte AS (SELECT * FROM foo WHERE foo.bar = True)
SELECT * FROM cte WHERE cte.id > 10 AND cte.id < 20

比postgresql

SELECT * FROM (SELECT * FROM foo WHERE bar = TRUE) cte
WHERE cte.id > 10 AND cte.id < 20

这是在外部查询中具有可选或动态谓词的考虑因素。更快的CTE版本将是

WITH cte AS (SELECT * FROM foo WHERE foo.bar = True AND foo.id > 10 AND foo.id < 20)
SELECT * FROM cte

这是设计使然,您可以依靠此行为来创建优化障碍。

只要允许SELECT子句,就允许使用CTE。因此,可以在INSERT,UPDATE或DELETE语句中使用CTE。 我不确定这是否是SQL标准的一部分

例如,在9.5版本和INSERT ... ON CONFLICT语法的引入之前,我们可以使用CTE来执行UPSERT。 Here's a SO thread that illustrates with an example

还有第二种更有趣的CTE类型,即RECURSIVE CTE,其中CTE由迭代部分和应用部分的并集组成,可以处理迭代部分中生成的值。我不认为这种类型的查询无论如何 内联