在PostgreSQL中(至少在9.4版本中)
通用表达式(CTEs)作为“优化栅栏”。
查询优化器不会将CTE项展开到外部查询中,也不会推送或拉取限定条件,即使在微不足道的情况下也是如此。因此,CTE项内部的未经限定的SELECT
语句总是会进行全表扫描(如果存在合适的索引,则进行索引扫描)。
因此,在PostgreSQL中,这两个概念是非常不同的,而一个简单的EXPLAIN
命令可以说明问题:
with parent as
(
select * from a101
)
select * from parent
where value1 = 159
并且
SELECT *
FROM
(
SELECT * FROM a101
) AS parent
WHERE value1 = 159;
然而,“将扫描整个表”并不一定意味着“将在内存中加载整个表”。PostgreSQL将使用TupleStore,随着TupleStore变得更大,它会透明地溢出到磁盘上的tempfile中。
最初的正当理由是计划(后来实现)CTE术语中的DML。如果CTE术语中有DML,则其执行非常重要且必须完整。如果CTE调用数据修改函数,这也可能是正确的。
不幸的是,似乎没有人想过“...但如果它只是一个SELECT,我们想要将其内联呢?”
社区中的许多人似乎将此视为一种特性,并经常将其作为优化器问题的解决方法进行传播。我发现这种态度非常令人困惑。因此,以后修复这个问题将非常困难,因为人们有意使用CTE来防止优化器改变查询。
换句话说,PostgreSQL滥用CTE作为伪查询提示(连同
OFFSET 0
hack一起),因为项目政策表示不需要或不支持真正的查询提示。
据我所知,MS SQL Server可以优化CTE屏障,但也可以选择将结果集材料化。