如果我有一个非常大的表,这个查询会在过滤重置之前将整个表加载到内存中:
with parent as
(
select * from a101
)
select * from parent
where value1 = 159
如您所见,父查询引用整个表。这会加载到内存中。这是查询的一个非常简化的版本。真正的查询与其他表有一些连接。我正在评估sql server 2012和postgrsql。
答案 0 :(得分:5)
在PostgreSQL中(至少从9.4开始)CTEs act as optimisation fences。
查询优化器不会将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;
但是,"会扫描整个表格"并不一定意味着"将整个表加载到内存中#34;。 PostgreSQL将使用TupleStore,当它变大时,它将透明地溢出到磁盘上的临时文件。
最初的理由是计划(后来实施)CTE术语中的DML。如果在CTE术语中有DML,那么它的执行是可预测和完整的,这是至关重要的。如果CTE调用数据修改函数,这也可能是真的。
不幸的是,似乎没有人想到" ...但如果它只是一个SELECT并且我们想要内联它呢?" 。
社区中的许多人似乎将此视为功能,并定期将其作为优化问题的解决方案发布。我觉得这种态度完全令人困惑。因此,以后很难解决此问题,因为人们在想要阻止优化器更改查询时故意使用CTE。
换句话说,PostgreSQL滥用CTE作为伪查询提示(以及OFFSET 0
hack),因为项目策略表示不需要或支持真正的查询提示。
AFAIK MS SQL Server可能会优化CTE障碍,但也可能选择实现结果集。
答案 1 :(得分:2)
我刚在PostgreSQL中为此查询做了EXPLAIN
。令人惊讶的是,它执行序列扫描而不是索引查找:
CTE Scan on parent (cost=123.30..132.97 rows=2 width=1711)
Filter: (value1 = 159)
CTE parent
-> Seq Scan on a101 (cost=0.00..123.30 rows=430 width=2060)
我在value1
上有一个主键索引,它用于简单的select * from a101 where value1 = 159
查询。
所以,答案是它会扫描整个表格。我很惊讶,我认为它将作为一个视图或子查询工作,但事实并非如此。您可以使用它来使用索引:
select * from (select * from a101) parent
where value1 = 159`
答案 2 :(得分:1)
没有。查询作为一个整体进行评估。如果查看执行计划,您将看到过滤器谓词将应用于内部搜索。外部搜索的微不足道,我相信它会被优化掉。
检查执行计划 - 这是基础知识,您最好快速学习如何做到这一点。一旦遇到真正的性能问题,您将需要找出问题,这就是执行计划的来源。
答案 3 :(得分:0)
CTE只是语言语法,使代码更具可读性,它们对查询执行性能没有影响。
执行查询时,将根据预定义的SQL Server查询执行阶段执行
1. FROM
2. ON
3. OUTER
4. WHERE
5. GROUP BY
6. CUBE | ROLLUP
7. HAVING
8. SELECT
9. DISTINCT
10 ORDER BY
11. TOP
首先应用WHERE
过滤器然后选择记录,这样就不会在内存中提取完整的表。