Oracle 上的大规模更新与合并性能

Dan*_*nga 3 oracle query-optimization sql-update sql-merge oracle12c

我正在尝试使用单个大量UPDATE语句从源表更新目标表,但执行时间比应有的时间长得多。

询问

UPDATE MY_DEST
SET (DEST_B, DEST_C) = (
  SELECT SRC_A + SRC_B, SRC_B
  FROM MY_SRC
  WHERE SRC_KEY = DEST_KEY AND SRC_DATE = DEST_DATE
);
Run Code Online (Sandbox Code Playgroud)

两个表都包含大约 10 到 1300 万行,它们具有匹配的主键,我们可以安全地假设目标表中的每一行在源表中都有对应的行。

表定义

CREATE TABLE MY_SRC (
  SRC_KEY VARCHAR2(50),
  SRC_DATE DATE,
  SRC_A NUMBER(15,2),
  SRC_B NUMBER(15,2),
  CONSTRAINT MY_SRC_PK PRIMARY KEY (SRC_KEY, SRC_DATE)
);

CREATE TABLE MY_DEST (
  DEST_KEY VARCHAR2(50),
  DEST_DATE DATE,
  DEST_B NUMBER(15,2),
  DEST_C NUMBER(15,2),
  CONSTRAINT MY_DEST_PK PRIMARY KEY (DEST_KEY, DEST_DATE)
);
Run Code Online (Sandbox Code Playgroud)

执行计划

Plan hash value: 3904754293

------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT             |           |    12M|   675M|   128M (20)| 01:23:55 |
|   1 |  UPDATE                      | MY_DEST   |       |       |            |          |
|   2 |   TABLE ACCESS FULL          | MY_DEST   |    12M|   675M| 69756   (1)| 00:00:03 |
|   3 |   TABLE ACCESS BY INDEX ROWID| MY_SRC    |     1 |    46 |     4   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | MY_SRC_PK |     1 |       |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

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

4 - access("SRC_KEY"=:B1 AND "SRC_DATE"=:B2)
Run Code Online (Sandbox Code Playgroud)

问题

  1. 上面的查询是否UPDATE无可救药地被迫使用缓慢的逐行执行计划?

  2. 我是否可以仅通过将其重写为语句来优化上面的查询MERGE,例如下面的语句?

    • 替代查询,重写为MERGE

      MERGE INTO MY_DEST
      USING (SELECT SRC_KEY, SRC_DATE, SRC_B, SRC_A + SRC_B AS SRC_C FROM MY_SRC)
      ON (DEST_KEY = SRC_KEY AND DEST_DATE = SRC_DATE)
      WHEN MATCHED THEN UPDATE SET DEST_B = SRC_B, DEST_C = SRC_C;
      
      Run Code Online (Sandbox Code Playgroud)
    • 备用执行计划

      Plan hash value: 2444580570
      
      ----------------------------------------------------------------------------------------
      | Id  | Operation            | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
      ----------------------------------------------------------------------------------------
      |   0 | MERGE STATEMENT      |         |    12M|   638M|       |   359K  (1)| 00:00:15 |
      |   1 |  MERGE               | MY_DEST |       |       |       |            |          |
      |   2 |   VIEW               |         |       |       |       |            |          |
      |*  3 |    HASH JOIN         |         |    12M|  2260M|   716M|   359K  (1)| 00:00:15 |
      |   4 |     TABLE ACCESS FULL| MY_SRC  |    12M|   568M|       |   162K  (1)| 00:00:07 |
      |   5 |     TABLE ACCESS FULL| MY_DEST |    12M|  1695M|       | 69756   (1)| 00:00:03 |
      ----------------------------------------------------------------------------------------
      
      Predicate Information (identified by operation id):
      ---------------------------------------------------
      
         3 - access("DEST_KEY"="SRC_KEY" AND "DEST_DATE"="SRC_DATE")
      
      Run Code Online (Sandbox Code Playgroud)
  3. MERGE使用 时,我可以获得与备用语句相同的良好性能吗UPDATE

我正在使用 Oracle 12c。

Mar*_*ber 8

1)是的,您永远不想在大型表上进行(估计)12M 次索引访问。

索引块主要位于磁盘上,因此您可能期望每秒访问 100 行。

即使由于缓存而每秒获得 1000 次索引访问,您仍然需要等待数小时才能处理 12M 行。

所以执行计划 1不适用于大表。

2)MERGE (带有两次全表扫描的哈希连接)的执行计划看起来不错并且应该可以工作。

3)是的,您可以UPDATE在设置中使用语句而不会出现问题。您将使用可更新连接视图的功能

询问

update (
select SRC_A, SRC_B,DEST_B, DEST_C 
from MY_DEST d
left outer join MY_SRC s
on d.DEST_KEY = s.SRC_KEY and d.DEST_DATE = s.SRC_DATE)
set DEST_B = SRC_A + SRC_B,
    DEST_C = SRC_B
; 
Run Code Online (Sandbox Code Playgroud)

执行计划

---------------------------------------------------------------------------------------
| Id  | Operation           | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT    |         |  1000K|    57M|       |  6682   (1)| 00:00:01 |
|   1 |  UPDATE             | MY_DEST |       |       |       |            |          |
|*  2 |   HASH JOIN OUTER   |         |  1000K|    57M|    40M|  6682   (1)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| MY_DEST |  1000K|    28M|       |  1341   (2)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| MY_SRC  |  1000K|    28M|       |  1341   (2)| 00:00:01 |
---------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("D"."DEST_DATE"="S"."SRC_DATE"(+) AND 
              "D"."DEST_KEY"="S"."SRC_KEY"(+))
          
Run Code Online (Sandbox Code Playgroud)

您会看到创建了与 类似的执行计划MERGE,因此您也会期望类似的性能。

最后的注释

您还可以使用并行提示来加快速度。

不要忘记您必须在会话中启用并行 dml 。

ALTER SESSION ENABLE PARALLEL DML;
Run Code Online (Sandbox Code Playgroud)

您的MERGE陈述不等于您的第一个UPDATE陈述。当目标表中存在主键但源表中不存在时,就会出现差异。

UPDATE目标列重置为,NULL同时MERGE让它们保持不变

我的UPDATE语句使用外连接,因此它的行为与您的UPDATE- 切换到内连接以获得MERGE行为。