sql – 为什么在WITH子句中的子查询重复时查询会更快?

假设一个项目使用分区来构建其数据.这个概念纯粹是针对业务的,与数据库分区无关.

假设业务逻辑确实如此:

>从output_table中删除,其中partition =< partitionX>
> insert into output_table(select * from input_table where partition =< partitionX>)

请记住,一切都是这样的结构,让问题复杂化(以达到实际问题).

假设我有一个潜在杀手的查询(SELECT查询),就时间而言:

insert into output_table (
  select * 
   from input_table
   left outer join additional_table additional_table1  
     on input_table.id = additional_table1.id
   left outer join additional_table additional_table2  
    on additional_table2.id = additional_table1.parent
  where partition =  <partitionX>
)

让我们优化它并探索选项.请记住每个表都有分区.另请注意table2如何连接两次,但是在不同的列上.而且,还要注意附加表是如何连接在一起的

一切都使用WITH子句,但有几个选项,我想知道为什么其中一个更好.

A. WITH部分中的直接和重复查询

WITH 
CACHED_input_table AS (
  SELECT *
  FROM input_table
  WHERE PARTITION_ID = < partition  X >
),
CACHED_additional_table1 AS (
  SELECT *
  FROM additional_table 
  WHERE PARTITION_ID = < partition  X >
),
CACHED_additional_table2 AS (
  SELECT *
  FROM additional_table 
  WHERE PARTITION_ID = < partition  X >
)
SELECT *    
FROM CACHED_input_table input_table
  LEFT OUTER JOIN CACHED_additional_table1 additional_table1 
    ON input_table.ID = additional_table1.ID
  LEFT OUTER JOIN CACHED_additional_table2 additional_table2 
    ON additional_table1.PARENT_ID = additional_table2.ID

B.在FROM部分中重用查询

WITH 
CACHED_input_table AS (
  SELECT *
  FROM input_table
  WHERE PARTITION_ID = < partition  X >
),
CACHED_additional_table AS (
  SELECT *
  FROM additional_table 
  WHERE PARTITION_ID = < partition  X >
)
SELECT *    
FROM CACHED_input_table input_table
  LEFT OUTER JOIN CACHED_additional_table additional_table1 
    ON input_table.ID = additional_table1.ID
  LEFT OUTER JOIN CACHED_additional_table additional_table2 
    ON additional_table1.PARENT_ID = additional_table2.ID

C.在WITH部分中重用查询

WITH 
CACHED_input_table AS (
  SELECT *
  FROM input_table
  WHERE PARTITION_ID = < partition  X >
),
CACHED_additional_table1 AS (
  SELECT *
  FROM additional_table 
  WHERE PARTITION_ID = < partition  X >
),
CACHED_additional_table2 AS (
  SELECT *
  FROM CACHED_additional_table1 
)
SELECT *    
FROM CACHED_input_table input_table

LEFT OUTER JOIN CACHED_additional_table1 additional_table1 
 ON input_table.ID = additional_table1.ID

LEFT OUTER JOIN CACHED_additional_table2 additional_table2 
 ON additional_table1.PARENT_ID = additional_table2.ID

根据经验,选项A是最快的.但为什么?有人可以解释一下吗?
(我正在使用Oracle v11.2)

我知道,我可能围绕这个公司特定的分区概念进行优化,与我所询问的围绕WITH子句的通用sql优化无关,但请将其作为一个真实的例子.

解释计划

选项A(7s中9900行)

------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name                         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |                              |     1 |  1037 | 18540   (8)| 00:00:03 |       |       |
|*  1 |  HASH JOIN OUTER        |                              |     1 |  1037 | 18540   (8)| 00:00:03 |       |       |
|*  2 |   HASH JOIN OUTER       |                              |     1 |   605 |  9271   (8)| 00:00:02 |       |       |
|   3 |    PARTITION LIST SINGLE|                              |     1 |   173 |     2   (0)| 00:00:01 |   KEY |   KEY |
|   4 |     TABLE ACCESS FULL   | input_table                  |     1 |   173 |     2   (0)| 00:00:01 |    24 |    24 |
|   5 |    PARTITION LIST SINGLE|                              |  1362K|   561M|  9248   (8)| 00:00:02 |   KEY |   KEY |
|   6 |     TABLE ACCESS FULL   | additional_table             |  1362K|   561M|  9248   (8)| 00:00:02 |    24 |    24 |
|   7 |   PARTITION LIST SINGLE |                              |  1362K|   561M|  9248   (8)| 00:00:02 |   KEY |   KEY |
|   8 |    TABLE ACCESS FULL    | additional_table             |  1362K|   561M|  9248   (8)| 00:00:02 |    24 |    24 |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("additional_table"."PARENT"="additional_table"."ID"(+))
   2 - access("input_table"."ID"="additional_table"."ID"(+))

选项B(10个9900行)

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                              |     1 |  2813 | 18186  (11)| 00:00:03 |       |       |
|   1 |  TEMP TABLE TRANSFORMATION |                              |       |       |            |          |       |       |
|   2 |   LOAD AS SELECT           | SYS_TEMP_0FD9D6CA2_C26AF925  |       |       |            |          |       |       |
|   3 |    PARTITION LIST SINGLE   |                              |  1362K|   561M|  9248   (8)| 00:00:02 |   KEY |   KEY |
|   4 |     TABLE ACCESS FULL      | additional_table1            |  1362K|   561M|  9248   (8)| 00:00:02 |    24 |    24 |
|*  5 |   HASH JOIN OUTER          |                              |     1 |  2813 |  8939  (15)| 00:00:02 |       |       |
|*  6 |    HASH JOIN OUTER         |                              |     1 |  1493 |  4470  (15)| 00:00:01 |       |       |
|   7 |     PARTITION LIST SINGLE  |                              |     1 |   173 |     2   (0)| 00:00:01 |   KEY |   KEY |
|   8 |      TABLE ACCESS FULL     | input_table                  |     1 |   173 |     2   (0)| 00:00:01 |    24 |    24 |
|   9 |     VIEW                   |                              |  1362K|  1714M|  4447  (14)| 00:00:01 |       |       |
|  10 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D6CA2_C26AF925  |  1362K|   561M|  4447  (14)| 00:00:01 |       |       |
|  11 |    VIEW                    |                              |  1362K|  1714M|  4447  (14)| 00:00:01 |       |       |
|  12 |     TABLE ACCESS FULL      | SYS_TEMP_0FD9D6CA2_C26AF925  |  1362K|   561M|  4447  (14)| 00:00:01 |       |       |
---------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("additional_table1"."PARENT"="additional_table2"."ID"(+))
   6 - access("input_table"."ID"="additional_table1"."ID"(+))

选项C(17s内9900行)

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                              |     1 |  2813 | 18186  (11)| 00:00:03 |       |       |
|   1 |  TEMP TABLE TRANSFORMATION |                              |       |       |            |          |       |       |
|   2 |   LOAD AS SELECT           | SYS_TEMP_0FD9D6CA7_C26AF925  |       |       |            |          |       |       |
|   3 |    PARTITION LIST SINGLE   |                              |  1362K|   561M|  9248   (8)| 00:00:02 |   KEY |   KEY |
|   4 |     TABLE ACCESS FULL      | additional_table             |  1362K|   561M|  9248   (8)| 00:00:02 |    24 |    24 |
|*  5 |   HASH JOIN OUTER          |                              |     1 |  2813 |  8939  (15)| 00:00:02 |       |       |
|*  6 |    HASH JOIN OUTER         |                              |     1 |  1493 |  4470  (15)| 00:00:01 |       |       |
|   7 |     PARTITION LIST SINGLE  |                              |     1 |   173 |     2   (0)| 00:00:01 |   KEY |   KEY |
|   8 |      TABLE ACCESS FULL     | input_table                  |     1 |   173 |     2   (0)| 00:00:01 |    24 |    24 |
|   9 |     VIEW                   |                              |  1362K|  1714M|  4447  (14)| 00:00:01 |       |       |
|  10 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D6CA7_C26AF925  |  1362K|   561M|  4447  (14)| 00:00:01 |       |       |
|  11 |    VIEW                    |                              |  1362K|  1714M|  4447  (14)| 00:00:01 |       |       |
|  12 |     TABLE ACCESS FULL      | SYS_TEMP_0FD9D6CA7_C26AF925  |  1362K|   561M|  4447  (14)| 00:00:01 |       |       |
---------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("additional_table1"."PARENT_ID"="CACHED_additional_table"."ID"(+))
   6 - access("input_table"."ID"="additional_table1"."ID"(+))

编辑:

>补充说明计划
>编辑后的基本查询:有一个input_table和一个additional_table,它连接两次,一次在input_table上,一次在自身上
>选项A的编辑查询:有一个input_table,additional_table连接两次,一次连接在input_table上,一次连接在一个自身的副本上(additional_table)
>选项B的编辑查询:有一个input_table,additional_table连接两次,一次在input_table上,一次连接,使用相同的别名(additional_table)
>选项C的已编辑查询:有一个input_table,additional_table连接两次,一次在input_table上,一次在另一个表上,在WITH部分中自己创建

最佳答案
查询A必须读取三个分区,一个是input_table的分区,另一个是additional_table的一个分区.

查询B必须读取两个分区,一个是input_table的分区,另一个是另一个分区的分区.然后,它必须将一个分区写入临时表并读取该临时表两次.

所以,假设估计没问题:
查询A读取input_table分区中的1行
  additional_table中有2次1362K行

查询B读取input_table分区中的1行
  在additional_table中临时表的3次1362K行
  写入1362K行.

如果优化器决定实现您的因子子查询,那么情况会更糟.顺便说一句,您可以通过使用内联提示来阻止实现.

转载注明原文:sql – 为什么在WITH子句中的子查询重复时查询会更快? - 代码日志