実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 / No.61 / ANSI JOINのおまけのおまけ
年を跨いで, ”実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 / No.61 / ANSI JOINのおまけ”のおまけです.
前回の投稿から間隔が空いていたので, まずは, 簡単な復習から.
ANSI構文のON句の結合条件でORが利用されているという, スーパータイプ, サブタイプテーブルの実装崩れというか, 大人の事情に押し切られて負けた感じありありの半端な状態.
あ, そうだ, Oralceの外部結合だとOR使えないけど, ANSIなら使えるじゃん!
という流れを感じるSQL文を, Oracleのオプティマイザは, LATERALへの書き換え(VW_LAT_E87C3AAF)や, OR EXPANDの書き換え(VW_ORE_FDF394AE)を駆使して, 物凄い最適化を行っていました.
この例では, 外部表, 内部表の多重度は, 1:0..1. かつ, スーパータイプ, サブタイプでいうところの不完全なサブタイプ.
さらに, 内部表は, 単純にニコイチにしただけのようなサブタイプテーブルで外部表との結合列が2列(おそらく本来同一列に統合されていただろう. . と思われる)ある. 惜しい!という感じのモデル.
比較的軽度のモデリング障害ではあるので, このまま使うのであれば, LATERAL変換されるのを避けるような書き換え, 比較的単純な HASH JOIN なるようにすればそこそこ改善できそうな感じはしますよね(いわゆるTemp落ちはある程度発生する前提で)
なお, この例で AUTO TRACEでの実行時間と, SQL MONITORの実行時間(DB内部)に差異があることに気づいた方もいると思いますが. これ, クライアントがデータをFETCHしている時間ですね. 行数が多いので. SQL*Plusの場合, デフォルトのFETCH SIZEが15なので行数が多いと, FETCHの際, サーバーからの受信で時間がかかります.
(この症状は以前, 高フェッチ圧症として紹介したこともあるので, 覚えている方も多いのではないかと思います. この例では1行の行サイズも大きめかつ, 行数も多めにして SELECT * にしているのでそこそこ目立つ時間になるようにしています. これも別のエントリーでネタにするための仕込みではあるのですが, 今回の記事では気にしないでください. SQLモニターのサーバー内部での純粋な処理時間だけで, 書き換え前後での差を見て行きます!)
SCOTT@orclpdb1> @dayx
1 SELECT *
2 FROM
3 supertype st
4 LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
5 ON
6 st.pkey = nmst.col1
7* OR st.pkey = nmst.col2
10001行が選択されました.
経過: 00:00:01.41
実行計画
----------------------------------------------------------
Plan hash value: 2133431102
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20002 | 20M| 40226 (1)| 00:00:02 |
| 1 | MERGE JOIN OUTER | | 20002 | 20M| 40226 (1)| 00:00:02 |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 10001 | 4971K| 204 (0)| 00:00:01 |
| 3 | BUFFER SORT | | 2 | 1082 | 40021 (1)| 00:00:02 |
| 4 | VIEW | VW_LAT_E87C3AAF | 2 | 1082 | 4 (0)| 00:00:01 |
| 5 | VIEW | VW_ORE_FDF394AE | 2 | 1082 | 4 (0)| 00:00:01 |
| 6 | UNION-ALL | | | | | |
| 7 | TABLE ACCESS BY INDEX ROWID| NIKOICHI_MITAINA_SUBTYPE | 1 | 1009 | 2 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | UK1 | 1 | | 1 (0)| 00:00:01 |
|* 9 | TABLE ACCESS BY INDEX ROWID| NIKOICHI_MITAINA_SUBTYPE | 1 | 1009 | 2 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | UK2 | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
8 - access("ST"."PKEY"="NMST"."COL1")
filter("NMST"."COL1" IS NOT NULL)
9 - filter(LNNVL("ST"."PKEY"="NMST"."COL1"))
10 - access("ST"."PKEY"="NMST"."COL2")
filter("NMST"."COL2" IS NOT NULL)
統計
----------------------------------------------------------
0 recursive calls
0 db block gets
32231 consistent gets
0 physical reads
0 redo size
15493776 bytes sent via SQL*Net to client (別エントリ向け)
7378 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client (別エントリ向け)
10001 sorts (memory)
0 sorts (disk)
10001 rows processed
1 SELECT /*+ MONITOR */ *
2 FROM
3 supertype st
4 LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
5 ON
6 st.pkey = nmst.col1
7* OR st.pkey = nmst.col2
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
--------------------------------------------------------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
SELECT /*+ MONITOR */ * FROM supertype st LEFT OUTER JOIN nikoichi_mitaina_subtype nmst ON st.pkey = nmst.col1 OR st.pkey = nmst.col2
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 3s(別ネタ向け仕込み)
...略...
Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.37 | 0.31 | 0.06 | 668 | 32231 |
=================================================
SQL Plan Monitoring Details (Plan Hash Value=2133431102)
=====================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 4 | +0 | 1 | 10001 | . | | |
| 1 | MERGE JOIN OUTER | | 20002 | 40226 | 4 | +0 | 1 | 10001 | . | | |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 10001 | 204 | 4 | +0 | 1 | 10001 | . | | |
| 3 | BUFFER SORT | | 2 | 40021 | 4 | +0 | 10001 | 10000 | 2048 | | |
| 4 | VIEW | VW_LAT_E87C3AAF | 2 | 4 | 4 | +0 | 10001 | 10000 | . | | |
| 5 | VIEW | VW_ORE_FDF394AE | 2 | 4 | 4 | +0 | 10001 | 10000 | . | | |
| 6 | UNION-ALL | | | | 4 | +0 | 10001 | 10000 | . | | |
| 7 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 2 | 4 | +0 | 10001 | 5000 | . | | |
| 8 | INDEX UNIQUE SCAN | UK1 | 1 | 1 | 4 | +0 | 10001 | 5000 | . | | |
| 9 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 2 | 4 | +0 | 10001 | 5000 | . | | |
| 10 | INDEX UNIQUE SCAN | UK2 | 1 | 1 | 4 | +0 | 10001 | 5000 | . | | |
=====================================================================================================================================================================
では, 書き換えて, LATERAL変換を避け, HASH JOINになるようにしてみましょう. (WITH句を利用していますが, 再利用ではなく読みやすさ狙いです. Oracleもそれを理解できるのでインラインビューとして扱われます)
今回のようなデータモデル障害の場合は, 治療もシンプルで良いのですがw(例に取り上げるのがメンドクサイやつだと, 解説するのもメンドクサイし良いことないので)
現場どのようになっているかを理解する必要があります. この例では, col1列とcol2列は実は同一列で良いだろうということになるので, 以下のように書き換えれば, JOIN ON ... OR なんて現時点のオプティマイザでは, ほぼ危険な感じしかしない実行計画になるようなSQLへの書き換えも回避できるのではないでしょうか?
結果は見ての通り, 別エントリ向けの仕込みであるFETCH時間を除いたデータベース内部のみの処理時間は, 0.37sec から 0.15secと62%ほど改善しています(ただ, このデータ量で私の環境だとPGA内に収まっているのでTemp落ちの影響は見えないですね. オンメモリなら勝ちは確実ですが)
1 WITH
2 t1 AS
3 (
4 SELECT
5 pkey
6 , CASE
7 WHEN col2 IS NULL
8 THEN col1
9 ELSE col2
10 END AS join_key
11 ,description
12 FROM
13 nikoichi_mitaina_subtype
14 WHERE
15 col1 IS NOT NULL
16 OR col2 IS NOT NULL
17 )
18 SELECT *
19 FROM
20 supertype st
21 LEFT OUTER JOIN t1
22 ON
23* st.pkey = t1.join_key
10001行が選択されました.
経過: 00:00:01.28
実行計画
----------------------------------------------------------
Plan hash value: 2223315184
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10001 | 14M| | 1224 (1)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 10001 | 14M| 5096K| 1224 (1)| 00:00:01 |
| 2 | TABLE ACCESS FULL| SUPERTYPE | 10001 | 4971K| | 204 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| NIKOICHI_MITAINA_SUBTYPE | 7500 | 7390K| | 410 (1)| 00:00:01 |
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ST"."PKEY"=CASE WHEN (ROWID(+) IS NOT NULL) THEN CASE WHEN ("COL2"(+) IS
NULL) THEN "COL1"(+) ELSE "COL2"(+) END ELSE NULL END )
3 - filter("COL1"(+) IS NOT NULL OR "COL2"(+) IS NOT NULL)
統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2748 consistent gets
0 physical reads
0 redo size
15483707 bytes sent via SQL*Net to client(別ネタ向け仕込み)
7378 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client(別ネタ向け仕込み)
0 sorts (memory)
0 sorts (disk)
10001 rows processed
1 WITH
2 t1 AS
3 (
4 SELECT
5 pkey
6 , CASE
7 WHEN col2 IS NULL
8 THEN col1
9 ELSE col2
10 END AS join_key
11 ,description
12 FROM
13 nikoichi_mitaina_subtype
14 WHERE
15 col1 IS NOT NULL
16 OR col2 IS NOT NULL
17 )
18 SELECT /*+ MONITOR */ *
19 FROM
20 supertype st
21 LEFT OUTER JOIN t1
22 ON
23* st.pkey = t1.join_key
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
----------------------------------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
WITH t1 AS ( SELECT pkey , CASE WHEN col2 IS NULL THEN col1 ELSE col2 END AS join_key ,description
FROM nikoichi_mitaina_subtype WHERE col1 IS NOT NULL OR col2 IS NOT NULL ) SELECT /*+ MONITOR */ *
FROM supertype st LEFT OUTER JOIN t1 ON st.pkey = t1.join_key
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 2s(別ネタ向け仕込み)
...略...
Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.15 | 0.13 | 0.02 | 668 | 2748 |
=================================================
SQL Plan Monitoring Details (Plan Hash Value=2223315184)
======================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
======================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 3 | +0 | 1 | 10001 | . | | |
| 1 | HASH JOIN OUTER | | 10001 | 1224 | 3 | +0 | 1 | 10001 | 7MB | | |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 10001 | 204 | 1 | +0 | 1 | 10001 | . | | |
| 3 | TABLE ACCESS FULL | NIKOICHI_MITAINA_SUBTYPE | 7500 | 410 | 3 | +0 | 1 | 10000 | . | | |
======================================================================================================================================================
将来のデータ量が100倍だとして. . . . HASH JOIN化してTemp落ちの影響も含めて見ておきましょう
SCOTT@orclpdb1> @dayx-1
1* DROP TABLE supertype
表が削除されました.
経過: 00:00:00.47
1 CREATE TABLE supertype
2 (
3 pkey NUMBER PRIMARY KEY
4 , attr1 NUMBER NOT NULL
5 , attr2 NUMBER NOT NULL
6 , note VARCHAR2(500)
7* )
表が作成されました.
経過: 00:00:00.15
1* DROP TABLE nikoichi_mitaina_subtype
表が削除されました.
経過: 00:00:00.05
1 CREATE TABLE nikoichi_mitaina_subtype
2 (
3 pkey NUMBER PRIMARY KEY
4 , col1 NUMBER
5 , col2 NUMBER
6 , description VARCHAR2(1000)
7 , CONSTRAINT uk1 unique (col1) USING INDEX
8 , CONSTRAINT uk2 unique (col2) USING INDEX
9* )
表が作成されました.
経過: 00:00:00.03
1 DECLARE
2 cMAX_ROWS CONSTANT NUMBER := 1000000;
3 BEGIN
4 FOR i IN 1..cMAX_ROWS LOOP
5 INSERT INTO supertype VALUES(i,0,0,LPAD(i,500,'*'));
6 INSERT INTO nikoichi_mitaina_subtype VALUES(
7 i
8 , CASE WHEN MOD(i,2) = 0 THEN i ELSE null END
9 , CASE WHEN MOD(i,2) = 1 THEN i ELSE null END
10 , LPAD(i,1000,'*')
11 );
12 IF MOD(i,100) = 0 THEN COMMIT; END IF;
13 END LOOP;
14 INSERT INTO supertype VALUES(cMAX_ROWS+1,0,0,null);
15 COMMIT;
16 DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'SUPERTYPE',no_invalidate=>false,cascade=>true);
17 DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'NIKOICHI_MITAINA_SUBTYPE',no_invalidate=>false,cascade=>true);
18* END;
PL/SQLプロシージャが正常に完了しました.
経過: 00:04:37.66
まず元ネタのLATERALとOR EXPAND書き換えされている方はどうか. . SQLモニターのExecが綺麗に増加(当然ですが)
SCOTT@orclpdb1> @dayx
1 SELECT *
2 FROM
3 supertype st
4 LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
5 ON
6 st.pkey = nmst.col1
7* OR st.pkey = nmst.col2
1000001行が選択されました.
経過: 00:02:23.03
実行計画
----------------------------------------------------------
Plan hash value: 2133431102
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2000K| 2004M| 6022K (1)| 00:03:56 |
| 1 | MERGE JOIN OUTER | | 2000K| 2004M| 6022K (1)| 00:03:56 |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 1000K| 486M| 19524 (1)| 00:00:01 |
| 3 | BUFFER SORT | | 2 | 1082 | 6002K (1)| 00:03:55 |
| 4 | VIEW | VW_LAT_E87C3AAF | 2 | 1082 | 6 (0)| 00:00:01 |
| 5 | VIEW | VW_ORE_FDF394AE | 2 | 1082 | 6 (0)| 00:00:01 |
| 6 | UNION-ALL | | | | | |
| 7 | TABLE ACCESS BY INDEX ROWID| NIKOICHI_MITAINA_SUBTYPE | 1 | 1012 | 3 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | UK1 | 1 | | 2 (0)| 00:00:01 |
|* 9 | TABLE ACCESS BY INDEX ROWID| NIKOICHI_MITAINA_SUBTYPE | 1 | 1012 | 3 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | UK2 | 1 | | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
8 - access("ST"."PKEY"="NMST"."COL1")
filter("NMST"."COL1" IS NOT NULL)
9 - filter(LNNVL("ST"."PKEY"="NMST"."COL1"))
10 - access("ST"."PKEY"="NMST"."COL2")
filter("NMST"."COL2" IS NOT NULL)
統計
----------------------------------------------------------
0 recursive calls
0 db block gets
3561836 consistent gets
71485 physical reads
0 redo size
1552273780 bytes sent via SQL*Net to client(別ネタ向け仕込み)
734925 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client(別ネタ向け仕込み)
1000001 sorts (memory)
0 sorts (disk)
1000001 rows processed
1 SELECT /*+ MONITOR */ *
2 FROM
3 supertype st
4 LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
5 ON
6 st.pkey = nmst.col1
7* OR st.pkey = nmst.col2
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
-----------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
SELECT /*+ MONITOR */ * FROM supertype st LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
ON st.pkey = nmst.col1 OR st.pkey = nmst.col2
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 219s(別ネタ向け仕込み)
...略...
Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 37 | 32 | 0.22 | 4.20 | 66668 | 4M | 573 | 558MB |
===========================================================================
SQL Plan Monitoring Details (Plan Hash Value=2133431102)
====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 220 | +0 | 1 | 1M | | | . | | |
| 1 | MERGE JOIN OUTER | | 2M | 6M | 220 | +0 | 1 | 1M | | | . | 6.90 | Cpu (2) |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 1M | 19524 | 220 | +0 | 1 | 1M | 573 | 558MB | . | 10.34 | Cpu (3) |
| 3 | BUFFER SORT | | 2 | 6M | 220 | +0 | 1M | 1M | | | 2048 | 17.24 | Cpu (5) |
| 4 | VIEW | VW_LAT_E87C3AAF | 2 | 6 | 220 | +0 | 1M | 1M | | | . | | |
| 5 | VIEW | VW_ORE_FDF394AE | 2 | 6 | 220 | +0 | 1M | 1M | | | . | | |
| 6 | UNION-ALL | | | | 220 | +0 | 1M | 1M | | | . | 6.90 | Cpu (2) |
| 7 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 3 | 220 | +0 | 1M | 500K | | | . | | |
| 8 | INDEX UNIQUE SCAN | UK1 | 1 | 2 | 220 | +0 | 1M | 500K | | | . | 17.24 | Cpu (5) |
| 9 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 3 | 220 | +0 | 1M | 500K | | | . | | |
| 10 | INDEX UNIQUE SCAN | UK2 | 1 | 2 | 220 | +0 | 1M | 500K | | | . | 6.90 | Cpu (2) |
====================================================================================================================================================================================
LATERAL変換を避け, HASH JOINにしてくれるような書き換えを行った方はどうかというと.
やはり, PGA内に収まっていたHASH JOINと比較して, Temp落ち(1GBほど)の影響で改善幅は減っていますが, 37sec が 32secと, 15%程度は勝っていますね. Temp落ちは避けられないですからね.
であれば, Temp落ちの落ちている先を速くすれば良いではないか. . . ということで, メモリにさえ余裕があれば, 使いすぎないようにした上で, tmpfs を使ってみましょうか. (一時表領域はなければOracleが再作成してくれるので)
1 WITH
2 t1 AS
3 (
4 SELECT
5 pkey
6 , CASE
7 WHEN col2 IS NULL
8 THEN col1
9 ELSE col2
10 END AS join_key
11 ,description
12 FROM
13 nikoichi_mitaina_subtype
14 WHERE
15 col1 IS NOT NULL
16 OR col2 IS NOT NULL
17 )
18 SELECT *
19 FROM
20 supertype st
21 LEFT OUTER JOIN t1
22 ON
23* st.pkey = t1.join_key
1000001行が選択されました.
経過: 00:02:24.20
実行計画
----------------------------------------------------------
Plan hash value: 2223315184
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 1451M| | 120K (1)| 00:00:05 |
|* 1 | HASH JOIN OUTER | | 1000K| 1451M| 497M| 120K (1)| 00:00:05 |
| 2 | TABLE ACCESS FULL| SUPERTYPE | 1000K| 486M| | 19524 (1)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| NIKOICHI_MITAINA_SUBTYPE | 750K| 723M| | 39559 (1)| 00:00:02 |
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ST"."PKEY"=CASE WHEN (ROWID(+) IS NOT NULL) THEN CASE WHEN ("COL2"(+) IS
NULL) THEN "COL1"(+) ELSE "COL2"(+) END ELSE NULL END )
3 - filter("COL1"(+) IS NOT NULL OR "COL2"(+) IS NOT NULL)
統計
----------------------------------------------------------
1297 recursive calls
0 db block gets
229696 consistent gets
374740 physical reads
0 redo size
1551273711 bytes sent via SQL*Net to client(別ネタ向け仕込み)
734925 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client(別ネタ向け仕込み)
0 sorts (memory)
0 sorts (disk)
1000001 rows processed
1 WITH
2 t1 AS
3 (
4 SELECT
5 pkey
6 , CASE
7 WHEN col2 IS NULL
8 THEN col1
9 ELSE col2
10 END AS join_key
11 ,description
12 FROM
13 nikoichi_mitaina_subtype
14 WHERE
15 col1 IS NOT NULL
16 OR col2 IS NOT NULL
17 )
18 SELECT /*+ MONITOR */ *
19 FROM
20 supertype st
21 LEFT OUTER JOIN t1
22 ON
23* st.pkey = t1.join_key
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
----------------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
WITH t1 AS ( SELECT pkey , CASE WHEN col2 IS NULL THEN col1 ELSE col2 END AS join_key
,description FROM nikoichi_mitaina_subtype WHERE col1 IS NOT NULL OR col2 IS NOT NULL )
SELECT /*+ MONITOR */ * FROM supertype st LEFT OUTER JOIN t1 ON st.pkey = t1.join_key
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 223s
...略...
Global Stats
================================================================================
| Elapsed | Cpu | IO | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
================================================================================
| 32 | 17 | 15 | 66668 | 230K | 6887 | 3GB | 5169 | 1GB |
================================================================================
SQL Plan Monitoring Details (Plan Hash Value=2223315184)
==============================================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
==============================================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 222 | +2 | 1 | 1M | | | | | . | . | | |
| 1 | HASH JOIN OUTER | | 1M | 120K | 223 | +1 | 1 | 1M | 5169 | 1GB | 5169 | 1GB | 184MB | 1GB | 90.62 | Cpu (9) |
| | | | | | | | | | | | | | | | | SQL*Net more data to client (7) |
| | | | | | | | | | | | | | | | | direct path write temp (13) |
| 3 | TABLE ACCESS FULL | NIKOICHI_MITAINA_SUBTYPE | 750K | 39559 | 67 | +7 | 1 | 1M | 1145 | 1GB | | | . | . | 6.25 | Cpu (2) |
==============================================================================================================================================================================================================
ということで, メモリに余裕があるので tmpfsを使って遊んでみましょう. 2GB固定サイズの一時表領域を作成して, SCOTTユーザーのデフォルト一時表領域にしました
一時表領域を作成して, scottのデフォルト一時表領域にする
[master@localhost ~]$ df -TH
ファイルシス タイプ サイズ 使用 残り 使用% マウント位置
tmpfs tmpfs 9.3G 17k 9.3G 1% /dev/shm
tmpfs tmpfs 9.3G 9.7M 9.3G 1% /run
tmpfs tmpfs 9.3G 0 9.3G 0% /sys/fs/cgroup
/dev/mapper/ol-root xfs 48G 44G 4.3G 92% /
/dev/mapper/ol-work xfs 11G 109M 11G 2% /work
...略...
[master@localhost ~]$ sudo mkdir /oratemp
...略...
[master@localhost ~]$ ls -l / | grep oratemp
drwxrwxrwt. 2 root root 60 1月 14 12:03 oratemp
[master@localhost ~]$ sudo mount -t tmpfs tmpfs /oratemp
[sudo] master のパスワード:
[master@localhost ~]$ df -TH
ファイルシス タイプ サイズ 使用 残り 使用% マウント位置
devtmpfs devtmpfs 9.3G 0 9.3G 0% /dev
tmpfs tmpfs 9.3G 17k 9.3G 1% /dev/shm
tmpfs tmpfs 9.3G 9.7M 9.3G 1% /run
tmpfs tmpfs 9.3G 0 9.3G 0% /sys/fs/cgroup
/dev/mapper/ol-root xfs 48G 44G 4.3G 92% /
/dev/mapper/ol-work xfs 11G 109M 11G 2% /work
...略...
tmpfs tmpfs 9.3G 0 9.3G 0% /oratemp
...略...
SYS@orclpdb1> create temporary tablespace hogetemp tempfile '/oratemp/hogetmp.dbf' size 2g;
表領域が作成されました.
SYS@orclpdb1> alter user scott temporary tablespace hogetemp;
ユーザーが変更されました.
では, オリジナルから. こちらそもそもTemp落ちしないので, LATERALビューへのアクセス回数が積み上がるだけなので, 該当表の物理読み込みが影響しなければほぼCPUタイムですね
SCOTT@orclpdb1> @dayx
1 SELECT /*+ MONITOR */ *
2 FROM
3 supertype st
4 LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
5 ON
6 st.pkey = nmst.col1
7* OR st.pkey = nmst.col2
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
SELECT /*+ MONITOR */ * FROM supertype st LEFT OUTER JOIN nikoichi_mitaina_subtype nmst
ON st.pkey = nmst.col1 OR st.pkey = nmst.col2
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 188s(別ネタ向け仕込み)
...略...
Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 39 | 34 | 0.21 | 4.62 | 66668 | 4M | 575 | 559MB |
===========================================================================
SQL Plan Monitoring Details (Plan Hash Value=2133431102)
=========================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
=========================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 189 | +0 | 1 | 1M | | | . | | |
| 1 | MERGE JOIN OUTER | | 2M | 6M | 189 | +0 | 1 | 1M | | | . | 2.78 | Cpu (1) |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 1M | 19593 | 189 | +0 | 1 | 1M | 575 | 559MB | . | 13.89 | Cpu (4) |
| | | | | | | | | | | | | | direct path read (1) |
| 3 | BUFFER SORT | | 2 | 6M | 189 | +0 | 1M | 1M | | | 2048 | 22.22 | Cpu (8) |
| 4 | VIEW | VW_LAT_E87C3AAF | 2 | 6 | 189 | +0 | 1M | 1M | | | . | | |
| 5 | VIEW | VW_ORE_FDF394AE | 2 | 6 | 189 | +0 | 1M | 1M | | | . | | |
| 6 | UNION-ALL | | | | 189 | +0 | 1M | 1M | | | . | 2.78 | Cpu (1) |
| 7 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 3 | 189 | +0 | 1M | 500K | | | . | 8.33 | Cpu (3) |
| 8 | INDEX UNIQUE SCAN | UK1 | 1 | 2 | 189 | +0 | 1M | 500K | | | . | 5.56 | Cpu (2) |
| 9 | TABLE ACCESS BY INDEX ROWID | NIKOICHI_MITAINA_SUBTYPE | 1 | 3 | 189 | +0 | 1M | 500K | | | . | 8.33 | Cpu (3) |
| 10 | INDEX UNIQUE SCAN | UK2 | 1 | 2 | 189 | +0 | 1M | 500K | | | . | 13.89 | Cpu (5) |
=========================================================================================================================================================================================
では, HASH JOINになるように書き換えた方のTemp落ちの時間は...想定通り短縮していますね. 15secほどあったIO Waits(s)ものが1/15程度まで減っています.
結果的に, 39sec -> 21secとなりました. Temp落ちする前提だから落ちた先のIOレイテンシーが小さければこうなるわけですけども. 逆に落ちた先のIOレイテンシーが大きければ影響も大きくなりますよね.
1 WITH
2 t1 AS
3 (
4 SELECT
5 pkey
6 , CASE
7 WHEN col2 IS NULL
8 THEN col1
9 ELSE col2
10 END AS join_key
11 ,description
12 FROM
13 nikoichi_mitaina_subtype
14 WHERE
15 col1 IS NOT NULL
16 OR col2 IS NOT NULL
17 )
18 SELECT /*+ MONITOR */ *
19 FROM
20 supertype st
21 LEFT OUTER JOIN t1
22 ON
23* st.pkey = t1.join_key
DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>NULL,TYPE=>'TEXT')
--------------------------------------------------------------------------------------------
SQL Monitoring Report
SQL Text
------------------------------
WITH t1 AS ( SELECT pkey , CASE WHEN col2 IS NULL THEN col1 ELSE col2 END AS join_key
,description FROM nikoichi_mitaina_subtype WHERE col1 IS NOT NULL OR col2 IS NOT NULL )
SELECT /*+ MONITOR */ * FROM supertype st LEFT OUTER JOIN t1 ON st.pkey = t1.join_key
Global Information
------------------------------
Status : DONE (ALL ROWS)
...略...
Duration : 211s(別ネタ向け仕込み)
...略...
Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 21 | 16 | 1.65 | 3.28 | 66668 | 247K | 5566 | 3GB | 3855 | 934MB |
===========================================================================================
SQL Plan Monitoring Details (Plan Hash Value=2223315184)
==============================================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
==============================================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 210 | +2 | 1 | 1M | | | | | . | . | | |
| 1 | HASH JOIN OUTER | | 1M | 119K | 211 | +1 | 1 | 1M | 3855 | 934MB | 3855 | 934MB | 367MB | 1GB | 85.71 | sort segment request (1) |
| | | | | | | | | | | | | | | | | Cpu (10) |
| | | | | | | | | | | | | | | | | SQL*Net message to client (1) |
| | | | | | | | | | | | | | | | | SQL*Net more data to client (6) |
| 2 | TABLE ACCESS FULL | SUPERTYPE | 1M | 19593 | 3 | +2 | 1 | 1M | 575 | 559MB | | | . | . | | |
| 3 | TABLE ACCESS FULL | NIKOICHI_MITAINA_SUBTYPE | 750K | 38796 | 120 | +4 | 1 | 1M | 1134 | 1GB | | | . | . | 14.29 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (1) |
==============================================================================================================================================================================================================
既存表定義を変えないでということになるとこの辺りが限界でしょうね.
オプティマイザは進化し続けていますが, モデル起因だったり構文起因だったり, まだまだ頑張っているけど, 何でもかんでもい感じに最適化できるわけではないので, モデリング頑張りましょうね. というのは不変ですよね. と思います.
今回の例は比較的単純かしていますが, 多重度が 1:* で結合カーディナリティが多くなるタイプや, スーパータイプ, サブタイプの共存的サブタイプだとさらに結合カーディナリティが増加するので, UNIONに分割してあげるなど別の手を駆使しないと対応しにくいタイプもあるので, 頭の片隅に置いておくと良さそうです.
2022年分のおまけのおまけ. これで, おしまい.
では, また.
Related article on Mac De Oracle
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 1 / TABLE FULL SCAN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 2 / INDEX UNIQUE SCAN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 3 / INDEX RANGE SCAN, Index Only Scan
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 4 / INDEX RANGE SCAN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 5 / INDEX RANGE SCAN, INLIST ITERATOR
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 6 / INDEX FAST SCAN, Index Only Scan
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 7 / INDEX FULL SCAN,Index Only Scan
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 8 / INDEX SKIP SCAN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 9 / TABLE ACCESS INMEMORY FULL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 10 / NESTED LOOP JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 11 / MERGE JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 12 / HASH JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 13 / HASH JOIN OUTER
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 14 / HASH JOIN FULL OUTER
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 15 / PX, TABLE ACCESS FULL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 16 / CONCATENATION
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 17 / SORT UNIQUE, UNION-ALL = UNION
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 18 / UNION-ALL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 19 / INTERSECTION
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 20 / MINUS
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 21 / WINDOW NOSORT STOPKEY
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 22 / COUNT STOPKEY
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 23 / HASH JOIN - LEFT-DEEP JOIN vs RIGHT-DEEP JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 24 / CONNECT BY NO FILTERING WITH START-WITH
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - Day 25 / UNION ALL (RECURSIVE WITH) DEPTH FIRST, RECURSIVE WITH PUMP
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - おまけ#1 / STAR TRANSFORM, VECTOR TRANSFORM (DWH向け)
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - おまけ#2 / MERGE (UPSERT)
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - おまけ#3 / RDFView
・実行計画は, SQL文のレントゲン写真だ! Oracle Database編 (全部俺)Advent Calendar 2019 - おまけ#4 / INDEX FULL SCAN (MIN/MAX) - Index Only Scan
・実行計画は, SQL文のレントゲン写真だ! No.30 - LOAD TABLE CONVENTIONAL vs. LOAD AS SELECT
・実行計画は, SQL文のレントゲン写真だ! No.31 - TEMP TABLE TRANSFORMATION LOAD AS SELECT (CURSOR DURATION MEMORY)
・実行計画は, SQL文のレントゲン写真だ! No.32 - EXTERNAL TABLE ACCESS FULL / INMEMORY FULL
・実行計画は, SQL文のレントゲン写真だ! No.33 - BITMAP CONVERSION TO ROWIDS
・実行計画は, SQL文のレントゲン写真だ! No.34 - 似て非なるもの USE_CONCAT と OR_EXPAND ヒント と 手書きSQLのレントゲンの見分け方
・実行計画は, SQL文のレントゲン写真だ! No.35 - 似て非なるもの USE_CONCAT と OR_EXPANDヒントとパラレルクエリー
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 1 / No.36 / INTERSECT ALL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 2 / No.37 / MINUS ALL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 3 / No.38 / EXCEPT and EXCEPT ALL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 4 / No.39 / In-Memory Hybrid Scans
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 5 / No.40 / PIVOT and UNPIVOT
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 6 / No.41 / In-Memory Vectorized Join
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 7 / No.42 / INDEX RANGE SCAN (MULTI VALUE)
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 8 / No.43 / TABLE ACCESS BY INDEX ROWID BATCHED
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 9 / No.44 / COLLECTION ITERATOR PICKLER FETCH
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 10 / No.45 / MAT_VIEW REWRITE ACCESS FULL
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 11 / No.46 / GROUPING SETS, ROLLUP, CUBE
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 12 / No.47 / TEMP TABLE TRANSFORMATION
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 13 / No.48 / MULTI-TABLE INSERT
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 14 / No.49 / the DUAL Table
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 15 / No.50 / REMOTE
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 16 / No.51 / Concurrent Execution of Union All and Union
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 17 / No.52 / Order by Elimination
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 18 / No.53 / Join Elimination
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 19 / No.54 / Group by Elimination
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 20 / No.55 / DISTINCT Elimination
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 21 / No.56 / INLIST ITERATOR と Sub Query と STATISTICS COLLECTOR
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 22 / No.57 / Subquery Unnesting
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 23 / No.58 / ANTI JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 24 / No.59 / SQL MACRO (19.7〜)
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 25 / No.60 / ANSI JOIN
・実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 / No.60 / ANSI JOINのおまけ
最近のコメント