以前から気になっていた、UPDATE文の処理速度検証を行いました。
UPDATE文で内部ビューを使用した場合と、EXISTS句を使った場合で、どちらが速いか検証しました。
前提条件
下記の検証SQL1、検証SQL2を使って検証を行いました。SQL文は異なりますが、同じ更新結果となります。
検証SQL1/内部ビューを使ったUPDATE文
UPDATE
(
SELECT
SAM.NAME AS OLD_NAME
,PAT.NAME AS NEW_NAME
FROM
TB_SAMPLE SAM
INNER JOIN WK_PATCH PAT ON PAT.ID = SAM.ID
) VW
SET
VW.OLD_NAME = VW.NEW_NAME
;
検証SQL2/EXISTS句を使ったUPDATE文
UPDATE
TB_SAMPLE SAM
SET
SAM.NAME = (
SELECT
PAT.NAME
FROM
WK_PATCH PAT
WHERE
PAT.ID = SAM.ID
)
WHERE
EXISTS (
SELECT
*
FROM
WK_PATCH PAT
WHERE
PAT.ID = SAM.ID
)
;
データ件数
TB_SAMPLEに100万レコード、WK_PATCHに10万レコードを用意しています。
テーブル定義
テーブル定義は下記になります。両テーブルのIDはPLAYMARY KEYになっています。
DESC TB_SAMPLE; 名前 NULL? 型 ------- -------- ------------------- ID NOT NULL NUMBER(10) NAME VARCHAR2(12 CHAR) DESC WK_PATCH; 名前 NULL? 型 ------- -------- ------------------- ID NOT NULL NUMBER(10) NAME VARCHAR2(12 CHAR)
処理速度検証結果
| 回数/平均 | 検証SQL1/内部ビュー | 検証SQL2/EXISTS |
|---|---|---|
| 1回目 | 7.17秒 | 6.83秒 |
| 2回目 | 8.26秒 | 6.80秒 |
| 3回目 | 6.07秒 | 6.81秒 |
| 4回目 | 7.69秒 | 7.18秒 |
| 5回目 | 8.80秒 | 6.09秒 |
| 平均 | 7.60秒 | 6.74秒 |
「EXISTS句を使ったUPDATE文」の方が処理速度的に優れています。10万レコードの更新で約1秒の違いがでます。
実行計画の違い
実行計画の比較もしてみました。
検証SQL1/内部ビューを使ったUPDATE文
--------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 1 | 38 | 4 (0)| 00:00:01 | | 1 | UPDATE | TB_SAMPLE | | | | | | 2 | NESTED LOOPS | | | | | | | 3 | NESTED LOOPS | | 1 | 38 | 4 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL | WK_PATCH | 1 | 19 | 3 (0)| 00:00:01 | |* 5 | INDEX UNIQUE SCAN | SYS_C007843 | 1 | | 0 (0)| 00:00:01 | | 6 | TABLE ACCESS BY INDEX ROWID| TB_SAMPLE | 1 | 19 | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------
検証SQL2/EXISTS句を使ったUPDATE文
-------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 1 | 22 | 5 (40)| 00:00:01 | | 1 | UPDATE | TB_SAMPLE | | | | | | 2 | NESTED LOOPS | | 1 | 22 | 3 (34)| 00:00:01 | | 3 | SORT UNIQUE | | 1 | 3 | 1 (0)| 00:00:01 | | 4 | INDEX FULL SCAN | SYS_C007844 | 1 | 3 | 1 (0)| 00:00:01 | |* 5 | INDEX UNIQUE SCAN | SYS_C007843 | 1 | 19 | 0 (0)| 00:00:01 | | 6 | TABLE ACCESS BY INDEX ROWID| WK_PATCH | 1 | 19 | 1 (0)| 00:00:01 | |* 7 | INDEX UNIQUE SCAN | SYS_C007844 | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------------
「検証SQL1/内部ビューを使ったUPDATE文」はWK_PATCHがTABLE ACCESS FULLになっていますが、「検証SQL2/EXISTS句を使ったUPDATE文」はINDEX UNIQUE SCANになっていますね。内部ビューの場合はなぜかINDEXが効かず、遅くなる原因になっているようです。
統計項目の違い
統計項目の比較結果になります。
| 統計項目 | 検証SQL1/内部ビュー | 検証SQL2/EXISTS |
|---|---|---|
| recursive calls | 18 | 9 |
| db block gets | 116512 | 103040 |
| consistent gets | 326273 | 203559 |
| physical reads | 0 | 0 |
| redo size | 40098192 | 31292992 |
| bytes sent via SQL*Net to client | 870 | 870 |
| bytes received via SQL*Net from client | 939 | 993 |
| SQL*Net roundtrips to/from client | 3 | 3 |
| sorts (memory) | 1 | 2 |
| sorts (disk) | 0 | 0 |
| rows processed | 100000 | 100000 |
TABLE ACCESS FULLの影響によるものだと思いますが、全体的に「検証SQL1/内部ビューを使ったUPDATE文」の方が値が大きくなっており負荷が高いと読み取れます。
結論
UPDATE文に処理速度を求めるのであれば「内部ビューを使ったUPDATE文」は使わずに「EXISTS句を使ったUPDATE文」を使って下さい。
個人的には「内部ビューを使ったUPDATE文」の方がSQLがすっきりするので好きなんですけどね。
