« 実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 22 / No.57 / Subquery Unnesting | トップページ | 実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 24 / No.59 / SQL MACRO (19.7〜) »

2022年12月23日 (金) / Author : Hiroshi Sekiguchi.

実行計画は, SQL文のレントゲン写真だ! Oracle Database (全部俺)Advent Calendar 2022 Day 23 / No.58 / ANTI JOIN

Day 22は, Subquery Unnestingを取り上げました. 最近は積極的にUnnestingしているように思うのですが, 皆さんはどう思いますか?

では, Day 23の窓を開けましょう!

今日は, 昨日話題にした, Subquery Unnesting の発動も必要ですが ANTI JOIN の実行計画という名のレントゲンから, いくつか特徴のある物をピックアップ(全バリエーション取れなかった言い訳w).

ANTI JOIN(結合されなかった主問い合わせの対象行を返す)もJOINのナカーマなであるわけで, Nested Loop Join, Hash Join, Merge joinがあり, さらに, NULLを考慮する必要のある結合列がないか, 一つか, それ以上かで, Null-Awareなし, NA(Null-Aware), SNA(Single Null-Aware) でOperation列に表示されるOperation名にいくつかのバリエーションがあります.
とはいえ, ANTI JOINかNULLを意識する必要があるかないか大きな分類で, JOIN方式は通常のJOINと同じ種類があるのはご存知の通り.

NOT EXISTS演算子や, NOT IN条件かつSubquery Unnestingが発動していると, 直感的にイメージできるようになってればいいかもしれないですね. かつ, NULLを意識してるなどもOperation名から見切れるとなお.
たった1文のOperationにそこまで情報が詰め込まれているんですよね.

参考)

Oracle Database 21c SQL Tuning Guide / 9.3.4 Antijoins

いつもと同じように 21c で確認します.

SCOTT@orclpdb1> select banner from v$version;

BANNER
----------------------------------------------------------------------
Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production


ヒントまで使ってなんとか, NESTED LOOPS ANTI が取れました(なかなか言うこと聞いてくれなかったw)
結合列でNULLを意識する必要のない場合に現れます.

HR@orclpdb1> @day23-2
1 SELECT
2 /*+
3 NO_INDEX(departments DEPT_ID_PK)
4 */
5 department_id
6 , department_name
7 FROM
8 departments
9 WHERE
10 department_id NOT IN
11 (
12 SELECT
13 /*+
14 NL_AJ
15 */
16 department_id
17 FROM
18 employees
19 WHERE
20 department_id IS NOT NULL
21* )

DEPARTMENT_ID DEPARTMENT_NAME
------------- ----------------------
120 Treasury
130 Corporate Tax
140 Control And Credit

...略...

240 Government Sales
250 Retail Sales
260 Recruiting
270 Payroll

16行が選択されました.

実行計画
----------------------------------------------------------
Plan hash value: 3082375452

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 323 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS ANTI | | 17 | 323 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| DEPARTMENTS | 27 | 432 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 41 | 123 | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

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

3 - access("DEPARTMENT_ID"="DEPARTMENT_ID")
filter("DEPARTMENT_ID" IS NOT NULL)

一つ前のSQL文とほぼ同じですが, IS NOT NULL条件を取り除いてあります.
NESTED LOOPS ANTI SNAです, SNAとなっているので, NULLを意識する必要のあることがわかります.

  1  SELECT
2 /*+
3 NO_INDEX(departments DEPT_ID_PK)
4 */
5 department_id
6 , department_name
7 FROM
8 departments
9 WHERE
10 department_id NOT IN
11 (
12 SELECT
13 department_id
14 FROM
15 employees
16* )

レコードが選択されませんでした.

実行計画
----------------------------------------------------------
Plan hash value: 2953329389

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 6 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS ANTI SNA| | 17 | 323 | 6 (50)| 00:00:01 |
| 3 | TABLE ACCESS FULL | DEPARTMENTS | 27 | 432 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 41 | 123 | 0 (0)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMPLOYEES | 1 | 3 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

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

1 - filter( NOT EXISTS (SELECT 0 FROM "EMPLOYEES" "EMPLOYEES" WHERE
"DEPARTMENT_ID" IS NULL))
4 - access("DEPARTMENT_ID"="DEPARTMENT_ID")
5 - filter("DEPARTMENT_ID" IS NULL)


MERGE JOIN ANTIが現れています. NA, SNAが現れていないことから, NULLは考慮しないANTI JOINであることがわかります.

SCOTT@orclpdb1> @day23

1 SELECT
2 deptno
3 ,dname
4 FROM
5 dept
6 WHERE
7 deptno NOT IN
8 (
9 SELECT
10 deptno
11 FROM
12 emp
13 WHERE
14 deptno IS NOT NULL
15* )

DEPTNO DNAME
---------- ------------------------------------------
40 OPERATIONS

実行計画
----------------------------------------------------------
Plan hash value: 1353548327

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN ANTI | | 1 | 16 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 52 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT UNIQUE | | 12 | 36 | 4 (25)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMP | 12 | 36 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

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

4 - access("DEPTNO"="DEPTNO")
filter("DEPTNO"="DEPTNO")
5 - filter("DEPTNO" IS NOT NULL)


MERGE JOIN ANTI NAが出ています. NAがあるので, NULLを考慮したMERGE JOIN ANTIであることがわかります. でもこれ本当は, SNAではないのか?(時間があったら10053でも追ってみようかなぁ)

  1  SELECT
2 deptno
3 ,dname
4 FROM
5 dept
6 WHERE
7 deptno NOT IN
8 (
9 SELECT
10 deptno
11 FROM
12 emp
13* )

DEPTNO DNAME
---------- ------------------------------------------
40 OPERATIONS

実行計画
----------------------------------------------------------
Plan hash value: 218628244

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN ANTI NA | | 1 | 16 | 6 (17)| 00:00:01 |
| 2 | SORT JOIN | | 4 | 52 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| DEPT | 4 | 52 | 2 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 5 | SORT UNIQUE | | 12 | 36 | 4 (25)| 00:00:01 |
| 6 | TABLE ACCESS FULL | EMP | 12 | 36 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

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

5 - access("DEPTNO"="DEPTNO")
filter("DEPTNO"="DEPTNO")


HASH JOIN ANTIが現れています. NULLを考慮させないために, IS NOT NULL条件を追加しています.

 1  SELECT
2 empno
3 ,ename
4 FROM
5 emp
6 WHERE
7 emp.deptno NOT IN
8 (
9 SELECT
10 /*+
11 HASH_AJ
12 */
13 dept.deptno
14 FROM
15 dept
16 WHERE
17 dept.deptno = 50
18 AND dept.deptno IS NOT NULL
19 )
20* AND emp.deptno IS NOT NULL

EMPNO ENAME
---------- ------------------------------
7499 ALLEN
7521 WARD

...略...

7369 SMITH
7566 JONES
7902 FORD

12行が選択されました.

実行計画
----------------------------------------------------------
Plan hash value: 4131168823

------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 176 | 3 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI | | 11 | 176 | 3 (0)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| EMP | 12 | 156 | 3 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN| PK_DEPT | 1 | 3 | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------

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

1 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")
2 - filter("EMP"."DEPTNO" IS NOT NULL)
3 - access("DEPT"."DEPTNO"=50)


HASH JOIN ANTI SNAが現れています. NULLを考慮する必要のあるSNAが付いた, HASH JOIN ANTIですね.

  1  SELECT
2 empno
3 ,ename
4 FROM
5 emp
6 WHERE
7 deptno NOT IN
8 (
9 SELECT
10 deptno
11 FROM
12 dept
13 WHERE
14 deptno = 50
15* )

EMPNO ENAME
---------- ------------------------------
7499 ALLEN
7521 WARD
7654 MARTIN

...略...

7369 SMITH
7566 JONES
7902 FORD

12行が選択されました.

実行計画
----------------------------------------------------------
Plan hash value: 2782438375

------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 176 | 3 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI SNA| | 11 | 176 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 12 | 156 | 3 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN| PK_DEPT | 1 | 3 | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------

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

1 - access("DEPTNO"="DEPTNO")
3 - access("DEPTNO"=50)


次は, HASH JOIN ANTI NAが出ているので, NULLを複数列で考慮してもらう必要があるので, 無理感はありますがw. 一応, 取れたw NA付きのHASH JOIN ANTIのOperationです.

SCOTT@orclpdb1> desc foo
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COL1 NOT NULL NUMBER
COL2 NOT NULL NUMBER
COL3 NUMBER

SCOTT@orclpdb1> desc bar
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COL0 NOT NULL NUMBER
COL1 NUMBER
COL2 NUMBER
COL4 NUMBER
 1  SELECT
2 col1
3 FROM
4 foo
5 WHERE
6 col3 NOT IN
7 (
8 SELECT
9 col2
10 FROM
11 bar
12* )

レコードが選択されませんでした.

実行計画
----------------------------------------------------------
Plan hash value: 3985729167

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 39 | 4 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI NA | | 1 | 39 | 4 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| FOO | 1 | 26 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| BAR | 1 | 13 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------

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

1 - access("COL3"="COL2")


狙った通りのOperationを行わせるのって難しいですねwwwwwwww (^^;;;;;;; NA, SNAまで考えるとw 

なかなか, これだけのバリエーション, 現場で見ることはないわけですけどもwww 今回のレントゲンシリーズで一番疲れたw
今日は, ここまで,

残り2日だ.

明日も, 頑張って, 窓を開きますよ. ここまできたらw



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

| |

コメント

コメントを書く