SELECT col, (SELECT COUNT(*) FROM table) as total_count FROM table
此查询为每一行执行子查询,对吧?
现在,如果我们有
;WITH CTE(total_count) AS (
SELECT COUNT(*) FROM table
)
SELECT col, (SELECT total_count FROM CTE) FROM table;
第二种方法会更有效吗? CTE只执行COUNT(*)
一次,然后SELECT将其用作准备值吗?或者在第二种情况下每行还执行了COUNT(*)
?
答案 0 :(得分:1)
对于Oracle,最可靠的方法是使用扩展统计信息来观察语句的行为。
首先将统计级别提高到ALL
alter session set statistics_level=all;
然后运行两个语句(获取所有行)和find the SQL_ID of those statements
最后使用以下语句显示统计信息(传递正确的SQL_ID):
select * from table(dbms_xplan.display_cursor('your SQL_ID here',null,'ALLSTATS LAST'));
这给出了我的测试表
SQL_ID 5n0sdcu8347j9, child number 0
-------------------------------------
SELECT col, (SELECT COUNT(*) FROM t1) as total_count FROM t1
Plan hash value: 1306093980
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 351 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 338 |
| 2 | TABLE ACCESS FULL| T1 | 1 | 1061 | 1000 |00:00:00.01 | 338 |
| 3 | TABLE ACCESS FULL | T1 | 1 | 1061 | 1000 |00:00:00.01 | 351 |
-------------------------------------------------------------------------------------
和
SQL_ID fs0h660f08bj6, child number 0
-------------------------------------
WITH CTE(total_count) AS ( SELECT COUNT(*) FROM t1 ) SELECT col,
(SELECT total_count FROM CTE) FROM t1
Plan hash value: 1223456497
--------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 351 |
| 1 | VIEW | | 1 | 1 | 1 |00:00:00.01 | 338 |
| 2 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 338 |
| 3 | TABLE ACCESS FULL| T1 | 1 | 1061 | 1000 |00:00:00.01 | 338 |
| 4 | TABLE ACCESS FULL | T1 | 1 | 1061 | 1000 |00:00:00.01 | 351 |
--------------------------------------------------------------------------------------
所以计划略有不同,但在这两种情况下FULL TABLE SCAN
只启动一次(列开始 = 1)。这并没有什么真正的区别。
为了比较的目的,我还运行了一个相关的子查询,它提供了一个完整的不同图片,其中包含大量的Starts(FTS)
SQL_ID cbvwd6pm6699m, child number 0
-------------------------------------
SELECT col, (SELECT COUNT(*) FROM t1 where col = a.col) as total_count
FROM t1 a
Plan hash value: 1306093980
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 351 |
| 1 | SORT AGGREGATE | | 1000 | 1 | 1000 |00:00:00.31 | 338K|
|* 2 | TABLE ACCESS FULL| T1 | 1000 | 11 | 1000 |00:00:00.31 | 338K|
| 3 | TABLE ACCESS FULL | T1 | 1 | 1061 | 1000 |00:00:00.01 | 351 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("COL"=:B1)
答案 1 :(得分:0)
我相信Oracle和SQL Server中的查询优化器都会识别计数查询不相关,计算一次,然后在外部查询执行过程中使用缓存结果。
另外,就我所知,CTE不会改变任何东西,因为在执行时它内部的代码基本上只是内联到实际的外部查询中。
这是Oracle的reference,它提到非相关子查询将被执行一次并缓存,除非外部查询只有几行。在这种情况下,它可能不会被缓存,因为在多次执行count子查询时没有多少惩罚。