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

Oracle上的大规模更新与合并性能,oracle,sql-update,query-optimization,oracle12c,sql-merge,Oracle,Sql Update,Query Optimization,Oracle12c,Sql Merge,我试图使用一个庞大的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 ); 这两个表都包含大约1000万到1300万行,它们有匹配的主键,我们可以安全地假设目标表中的每一行在源表中都有对应的行 表格定义 CR

我试图使用一个庞大的
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
);
这两个表都包含大约1000万到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)
);
执行计划

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)
问题

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)
);
  • 上面的
    UPDATE
    查询是否无望地被迫使用缓慢的逐行执行计划

  • 我能否只通过将上面的查询重写为
    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;
      
    • 替代执行计划

      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")
      
      ---------------------------------------------------------------------------------------
      | 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"(+))
                
      
  • 使用
    更新
    时,我能否获得与备用
    合并
    语句相同的良好性能

  • 我正在使用Oracle 12c。

    1)是的,您永远不希望在一个大表上进行(估计)1200万次索引访问

    索引块大部分位于光盘上,因此您可能希望每秒访问100行

    即使由于缓存的原因每秒获得1000个索引访问, 处理1200万行仍然需要等待数小时

    因此,对于大型表,执行计划1是禁止的

    2)合并的执行计划(两次完整表扫描的哈希连接)看起来不错,应该可以工作

    3)是的,您可以使用
    UPDATE
    语句,在设置中不会出现问题。您将使用

    质疑

    执行计划

    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")
    
    ---------------------------------------------------------------------------------------
    | 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"(+))
              
    
    您可以看到,为
    合并创建了类似的执行计划,因此您也将看到类似的性能

    最终注释

    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)
    );
    
    您还可以使用并行提示来加速

    不要忘记您必须在会话中启用

    ALTER SESSION ENABLE PARALLEL DML;
    
    您的
    MERGE
    语句与第一个
    UPDATE
    语句不相等。 当目标表中存在主键而源表中不存在主键时,会出现差异

    UPDATE
    将目标列重置为
    NULL
    ,而
    MERGE
    将其保持不变


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

    您的MERGE语句是否比UPDATE语句运行得更快?如果是的话,我会用它来代替。通过在
    when matched
    子句中添加谓词以仅在必要时更新,例如
    where dest_b!=src_b或dest_c!=src_c
    。如果所有或大多数行匹配,性能将不会有太大的改善,但如果这是一个频繁运行的过程(例如,每天),您可能会看到一些好处。@ Boeistor这是一个很好的点,与预处理有关,但你不应该伪造列是代码> null <代码>,并考虑它在你的谓词。例如'nvl(dest_b,'x')!=nvl(src_b,'x'),以获得原始语句的确切行为。@MarmiteBomber精彩的一点!