CTE与子查询,哪一个有效?

时间:2017-04-12 12:47:08

标签: sql database oracle

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(*)

2 个答案:

答案 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子查询时没有多少惩罚。