1. 程式人生 > >UPDATE多表關聯更新時為什麼會慢

UPDATE多表關聯更新時為什麼會慢

構建環境如下:

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 
Connected as [email protected]:1521/orcl

SQL> CREATE TABLE test1 AS SELECT * FROM Dba_Objects;
Table created
SQL> CREATE TABLE test2 AS SELECT * FROM Dba_Objects;
Table created
SQL> CREATE TABLE test3 AS SELECT * FROM Dba_Objects;
Table created
SQL> ALTER TABLE test2 ADD CONSTRAINTS pk_test2 PRIMARY KEY (object_id);
Table altered
SQL> ALTER TABLE test3 ADD CONSTRAINTS pk_test3 PRIMARY KEY (object_id);
Table altered

我們先來看一個查詢的plan
SQL> explain plan FOR SELECT a.object_name,
  2         (SELECT b.object_name FROM test2 b WHERE b.object_id = a.object_id) AS new_name,
  3         a.object_type,
  4         (SELECT c.object_name FROM test3 c WHERE c.object_id = a.object_id) AS new_type
  5    FROM test1 a
  6   WHERE EXISTS (SELECT NULL FROM test2 b WHERE b.object_id = a.object_id)
  7     AND EXISTS
  8   (SELECT NULL FROM test3 c WHERE c.object_id = a.object_id);
Explained

SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2412657849
--------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          | 56357 |  6384K|   326  (12)| 00
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST2    |     1 |    79 |     2   (0)| 00
|*  2 |   INDEX UNIQUE SCAN         | PK_TEST2 |     1 |       |     1   (0)| 00
|   3 |  TABLE ACCESS BY INDEX ROWID| TEST3    |     1 |    79 |     2   (0)| 00
|*  4 |   INDEX UNIQUE SCAN         | PK_TEST3 |     1 |       |     1   (0)| 00
|   5 |  NESTED LOOPS SEMI          |          | 56357 |  6384K|   326  (12)| 00
|   6 |   NESTED LOOPS SEMI         |          | 59647 |  5999K|   312   (8)| 00
|   7 |    TABLE ACCESS FULL        | TEST1    | 80164 |  7045K|   293   (2)| 00
|*  8 |    INDEX UNIQUE SCAN        | PK_TEST3 | 44380 |   563K|     0   (0)| 00
|*  9 |   INDEX UNIQUE SCAN         | PK_TEST2 | 71566 |   908K|     0   (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
   2 - access("B"."OBJECT_ID"=:B1)
   4 - access("C"."OBJECT_ID"=:B1)
   8 - access("C"."OBJECT_ID"="A"."OBJECT_ID")
   9 - access("B"."OBJECT_ID"="A"."OBJECT_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)
28 rows selected
注意ID=1、2、3、4這幾行PLAN。對應的分別就是

(SELECT b.object_name FROM test2 b WHERE b.object_id = a.object_id) AS new_name,

(SELECT c.object_name FROM test3 c WHERE c.object_id = a.object_id) AS new_type

兩個語句

下面是一個UPDATE的PLAN

SQL> explain plan for UPDATE test1 a
  2     SET a.object_name =
  3         (SELECT b.object_name FROM test2 b WHERE b.object_id = a.object_id),
  4         a.object_type =
  5         (SELECT c.object_type FROM test3 c WHERE c.object_id = a.object_id)
  6   WHERE EXISTS (SELECT NULL FROM test2 b WHERE b.object_id = a.object_id)
  7     AND EXISTS
  8   (SELECT NULL FROM test3 c WHERE c.object_id = a.object_id);
Explained

SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2604142680
--------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes | Cost (%CPU)| T
--------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT             |          | 56357 |  6384K|   338K (34)| 0
|   1 |  UPDATE                      | TEST1    |       |       |            |
|   2 |   NESTED LOOPS SEMI          |          | 56357 |  6384K|   326  (12)| 0
|   3 |    NESTED LOOPS SEMI         |          | 59647 |  5999K|   312   (8)| 0
|   4 |     TABLE ACCESS FULL        | TEST1    | 80164 |  7045K|   293   (2)| 0
|*  5 |     INDEX UNIQUE SCAN        | PK_TEST3 | 44380 |   563K|     0   (0)| 0
|*  6 |    INDEX UNIQUE SCAN         | PK_TEST2 | 71566 |   908K|     0   (0)| 0
|   7 |   TABLE ACCESS BY INDEX ROWID| TEST2    |     1 |    79 |     2   (0)| 0
|*  8 |    INDEX UNIQUE SCAN         | PK_TEST2 |     1 |       |     1   (0)| 0
|   9 |   TABLE ACCESS BY INDEX ROWID| TEST3    |     1 |    24 |     2   (0)| 0
|* 10 |    INDEX UNIQUE SCAN         | PK_TEST3 |     1 |       |     1   (0)| 0
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
   5 - access("C"."OBJECT_ID"="A"."OBJECT_ID")
   6 - access("B"."OBJECT_ID"="A"."OBJECT_ID")
   8 - access("B"."OBJECT_ID"=:B1)
  10 - access("C"."OBJECT_ID"=:B1)
Note
-----
   - dynamic sampling used for this statement (level=2)
29 rows selected
注意這兒的ID=7、8、9、10

對應的分別就是

a.object_name = (SELECT b.object_name FROM test2 b WHERE b.object_id = a.object_id),

a.object_type = (SELECT c.object_type FROM test3 c WHERE c.object_id = a.object_id)

大家仔細對比這兩個語句,看下UPDATE的多表關聯更新是不是與標量子查詢很象。

那麼標題量子查詢慢的時候,對應的UPDATE慢也就不稀奇了。