トップ  > SQL調査報告  > 条件句に OR句 と AND句 を使用したSELECT文の実行計画を検証
 



■ 条件句に OR句 と AND句 を使用したSELECT文の実行計画を検証  ヽ(●`⌒´メ)ノ
■ 調査テーマ
 OR句 と AND句 を両方使うSQL文では、けっこうバグが出ます。ちゃんと動作確認してからプログ
ラムに組み込みましょう。

エエッ!?(●・ロ・●ノ)ノバグ?

心配するなかれ。次のSELECT文の意味の違いを説明できれば、なんの心配も御座いません。

      SELECT * FROM テーブル WHERE カラム1 = 10 OR カラム1 = 20 AND カラム2 = 100;

      SELECT * FROM テーブル WHERE カラム1 = 20 OR カラム1 = 10 AND カラム2 = 100;

大丈夫だよね?

 OR句 と AND句 の両方を使用したSELECT文の挙動を調べ、実際どの様な実行計画なのかを調べる。
■ 調査手順
< 手順1 >
 次のテーブルを用意する。
    CREATE TABLE TMP_01 (
      NUM1    NUMBER
    , NUM2    NUMBER);

< 手順2 >
 次のレコードをINSERTする。
    INSERT INTO TMP_01 VALUES( 1,0 );
    INSERT INTO TMP_01 VALUES( 1,1 );
    INSERT INTO TMP_01 VALUES( 1,2 );
    INSERT INTO TMP_01 VALUES( 1,3 );
    INSERT INTO TMP_01 VALUES( 2,0 );
    INSERT INTO TMP_01 VALUES( 2,1 );
    INSERT INTO TMP_01 VALUES( 2,2 );
    INSERT INTO TMP_01 VALUES( 2,3 );
    INSERT INTO TMP_01 VALUES( 3,0 );
    INSERT INTO TMP_01 VALUES( 3,1 );
    COMMIT ;

< 手順3 >
 次のSELECT文を実行し、SELECTされる結果と実行計画を確認する。
    (1)
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM1 = 1 
    OR NUM1 = 2
    AND NUM2 = 0;

    (2)
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM2 = 0 
    OR NUM1 = 1
    OR NUM1 = 2;

    (3)
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM1 = 1 
    AND NUM2 = 1
    OR NUM1 = 2
    AND NUM2 = 2;

< 手順4 >
 次のINDEXを作成する。
    CREATE INDEX TMP_01_IDX_01 ON TMP_01 ( NUM1 );

< 手順5 >
 次のSELECT文を実行し、SELECTされる結果と実行計画を確認する。
    (1) < 手順3 > (1) の結果と比較
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM1 = 1 
    OR NUM1 = 2
    AND NUM2 = 0;

    (2) < 手順3 > (2) の結果と比較
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM2 = 0 
    OR NUM1 = 1
    OR NUM1 = 2;

    (3) < 手順3 > (3) の結果と比較
    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE NUM1 = 1 
    AND NUM2 = 1
    OR NUM1 = 2
    AND NUM2 = 2;
■ 調査結果
 SELECT文の実行結果は以下のようになりました。
< 手順3(1) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM1 = 1 
OR NUM1 = 2
AND NUM2 = 0;

      NUM1       NUM2
---------- ----------
         1          0
         1          1
         1          2
         1          3
         2          0
5行が選択されました。

SQL>

< 手順3(1) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   TABLE ACCESS (FULL) OF 'TMP_01'

NUM1 = 1 のものが選択され、NUM1 = 2 且つ NUM2 = 0 が選択されている。
次のように括弧条件が括られているイメージです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM1 = 1 )
    OR ( NUM1 = 2
    AND NUM2 = 0 );

< 手順3(2) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM2 = 0 
OR NUM1 = 1
OR NUM1 = 2;

      NUM1       NUM2
---------- ----------
         1          0
         1          1
         1          2
         1          3
         2          0
         2          1
         2          2
         2          3
         3          0 この行が選択されていることで一瞬「おっ!?」って思いませんか?
         2          0
10行が選択されました。

SQL>

< 手順3(2) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   TABLE ACCESS (FULL) OF 'TMP_01'

次のように括弧条件が括られているイメージです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM2 = 0 )
    OR ( NUM1 = 1 )
    OR ( NUM1 = 2 );

< 手順3(3) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM1 = 1 
AND NUM2 = 1
OR NUM1 = 2
AND NUM2 = 2;

      NUM1       NUM2
---------- ----------
         1          1
         2          2
2行が選択されました。

SQL>

< 手順3(3) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   TABLE ACCESS (FULL) OF 'TMP_01'

次のように括弧条件が括られているイメージです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM1 = 1
    AND NUM2 = 1 ) 
    OR ( NUM1 = 2
    AND NUM2 = 2 );

< 手順5(1) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM1 = 1 
OR NUM1 = 2
AND NUM2 = 0;

      NUM1       NUM2
---------- ----------
         2          0
         1          0
         1          1
         1          2
         1          3
5行が選択されました。

SQL>

< 手順5(1) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   CONCATENATION
    2    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_01'
    3    2       INDEX (RANGE SCAN) OF 'TMP_01_IDX_01' (NON-UNIQUE)
    4    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_01'
    5    4       INDEX (RANGE SCAN) OF 'TMP_01_IDX_01' (NON-UNIQUE)

NUM1 = 2 且つ NUM2 = 0 のものが選択され、NUM1 = 1 が選択されている。
CONCATENATION は避けられないようだ。
SQL文の結果セットの返えす順番がDBから取り出された順番であることは保証されていないことは承知
していますが、だいたいは取り出された順番のようだ。NUM1のINDEX検索によってWHERE条件句の下から
実行されている。
次のように括弧条件が括られているイメージです。< 手順3 > (1) と同じです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM1 = 1 )
    OR ( NUM1 = 2
    AND NUM2 = 0 );

< 手順5(2) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM2 = 0 
OR NUM1 = 1
OR NUM1 = 2;

      NUM1       NUM2
---------- ----------
         1          0
         1          1
         1          2
         1          3
         2          0
         2          1
         2          2
         2          3
         3          0
         2          0
10行が選択されました。

SQL>

< 手順5(2) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   TABLE ACCESS (FULL) OF 'TMP_01'

FULL アクセスですね。SELECT句に ヒント句 /*+ INDEX (TMP_01 TMP_01_IDX_01) */ を入れても
INDEX検索にならなかった。なんでだろう ?
次のように括弧条件が括られているイメージです。< 手順3 > (2) と結果セットも実行計画も同じです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM2 = 0 )
    OR ( NUM1 = 1 )
    OR ( NUM1 = 2 );

< 手順5(3) 結果 >
SQL>  SELECT NUM1, NUM2 
FROM TMP_01
WHERE NUM1 = 1 
AND NUM2 = 1
OR NUM1 = 2
AND NUM2 = 2;

      NUM1       NUM2
---------- ----------
         2          2
         1          1
2行が選択されました。

SQL>

< 手順5(3) 実行計画 >
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=RULE
    1    0   CONCATENATION
    2    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_01'
    3    2       INDEX (RANGE SCAN) OF 'TMP_01_IDX_01' (NON-UNIQUE)
    4    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_01'
    5    4       INDEX (RANGE SCAN) OF 'TMP_01_IDX_01' (NON-UNIQUE)

CONCATENATION は避けられないようだ。
SQL文の結果セットの返えす順番がDBから取り出された順番であることは保証されていないことは承知
していますが、だいたいは取り出された順番のようだ。NUM1のINDEX検索によってWHERE条件句の下から
実行されている。
次のように括弧条件が括られているイメージです。< 手順3 > (3) と同じです。

    SELECT NUM1, NUM2 
    FROM TMP_01
    WHERE ( NUM1 = 1
    AND NUM2 = 1 ) 
    OR ( NUM1 = 2
    AND NUM2 = 2 );
■ まとめと感想
 WHERE句 に OR条件 が現れると、次のOR句が現れるまでもしくは条件の最後までを( ) で括った
SELECT文が実行されるようだ。

 結果、やっぱりOR句 は難しい。CONCATENATION も行われるし極力避けたい。

 (●・д・●)ノ アウト