一次針對Not in的優化--如何讓其選擇hash join anti

首先來看兩個sql,返回結果相同,但是耗時差別很大

SQL> select count(*)
  2             fromjustin_good r
  3            where notexists
  4                 (select 'x' from justin_count pc where pc.id =r.justin_good_id)
  5  ;

  COUNT(*)
----------
      7229

Executed in 3.437 seconds

SQL> select count(*)
  2             fromjustin_good r
  3            wherer.justin_good_id not in
  4                 (select pc.id from justin_count pc)
  5  ;

  COUNT(*)
----------
      7229

Executed in 128.203 seconds

再來看一下它們的執行計劃
使用not exist的語句cost3452,而not in 的卻達到14216
SQL> explain plan for select count(*)
  2             fromjustin_good r
  3            where notexists
  4                 (select 'x' from justin_count pc where pc.id =r.justin_good_id);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------
Plan hash value: 1087925722

Id

Operation

Name

Rows

Bytes

Cost(%CPU)

Time

0

SELECT STATEMENT   

           

1

9

3452(2)

00:00:42

1

SORT AGGREGATE     

           

1

9

 

       

2

HASH JOIN RIGHT ANTI

           

59

531

3452(2)

00:00:42

3

INDEX FAST FULL SCAN

PK11_1

4562

22810

4(0)

00:00:01

4

TABLE ACCESS FULL  

justin_good

602K

2355K

3440(2)

00:00:42

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 -access("PC"."ID"="R"."justin_good_id")
16 rows selected.

SQL> explain plan for select count(*)
  2             fromjustin_good r
  3            wherer.justin_good_id not in
  4                 (select pc.id from justin_count pc);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------Planhash value: 4119029611

Id

Operation

Name

Rows

Bytes

Cost(%CPU)

Time

0

SELECT STATEMENT

           

  1

   4

14216   (2)

00:00:42

1

SORT AGGREGATE

           

  1

   4

          

       

2

FILTER

           

   

    

          

00:00:42

3

TABLE ACCESS FULL

PK11_1

602K

2355K

 3442   (2)

00:00:01

4

INDEX FULL SCAN

justin_good

  1

   5

   11   (0)

00:00:42

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
 2 - filter( NOT EXISTS (SELECT 0 FROM "justin"."justin_count""PC" WHERE
             LNNVL("PC"."ID"<>:B1)))
 4 - filter(LNNVL("PC"."ID"<>:B1))
18 rows selected.

 

當使用 where a not in (select …)的時候,必須確保該列a的返回值沒有null,否則無法進行hash join anti.
解決辦法:
1
  爲該列加not null約束
2
  改寫sql,not exists

 

oracle解析where a not in (select )之類的sql時,會查看列a是否存在null,只有當確保不存在的時候,纔會考慮將Not in轉換爲anti-join;然後進行一系列諸如nest loop join,merge joincost對比,最終敲定執行計劃

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章