Oracleは、インデックス定義に含まれる列の全てがNULL
の
時は、その行をインデックスに含めません。つまり、全てのインデックスがwhere
句が以下のような部分インデックスであると言えます。
CREATE INDEX idx
ON tbl (A, B, C, ...)
WHERE A IS NOT NULL
OR B IS NOT NULL
OR C IS NOT NULL
...
EMP_DOB
という、DATE_OF_BIRTH
列のみの
インデックスを考えてみましょう。DATE_OF_BIRTH
の値がない行は、インデックスに追加される
事はありません。
INSERT INTO employees ( subsidiary_id, employee_id
, first_name , last_name
, phone_number)
VALUES ( ?, ?, ?, ?, ? )
このinsert
文はDATE_OF_BIRTH
を
指定していないので、デフォルトのNULL
なります。そのため、
このレコードはEMP_DOB
インデックスには追加されません。
結果としてこのインデックスは、DATE_OF_BIRTH
IS NULL
のレコードを検索するクエリをサポートできなくなります。
SELECT first_name, last_name
FROM employees
WHERE date_of_birth IS NULL
そのような結果にも関わらず、インデックスに含まれる列の
最低でも1つがNULL
ではない場合は、複合インデックスにそこのレコードは追加されます。
CREATE INDEX demo_null
ON employees (subsidiary_id, date_of_birth)
SUBSIDIARY_ID
がNULL
ではないので、
上の行はインデックスに追加されます。そのためこのインデックスは、
ある子会社に属するDATE_OF_BIRTH
を持たない全従業員を検索するクエリをサポートできるのです。
SELECT first_name, last_name
FROM employees
WHERE subsidiary_id = ?
AND date_of_birth IS NULL
このインデックスがwhere
句の全体を
カバーしていることに注意しましょう。全てのフィルタがINDEX RANGE SCAN
の中でアクセス述語として
使われています。
協力してください
この記事が気に入ったら、私の書いた本「SQLパフォーマンス詳解」や私によるトレーニングもきっと気にいるはず。
DATE_OF_BIRTH
IS NULL
になっている
レコードを検索するために、元のクエリにもこの考え方を適用することが
できます。そのためには、DATE_OF_BIRTH
の条件がアクセス述語に使えるように、インデックスの一番左になくては
なりません。実際には、2番目以降の列はクエリには必要ありませんが、
インデックスが全ての行を含むように、NULL
にはなり得ない
列を追加しましょう。SUBSIDIARY_ID
のような、NOT
NULL
制約の付いた列ならどれでも使うことができます。
代わりに、NULL
になることのない定数表現も
使えます。そうすれば、DATE_OF_BIRTH
がNULL
でも
インデックスは全行を含むようにできます。
DROP INDEX emp_dob
CREATE INDEX emp_dob ON employees (date_of_birth, 'X')
技術的には、このインデックスは関数インデックスです。この例によって、
OracleではNULL
を含むインデックスは作れないという都市伝説が誤りであることを証明できます。
ヒント
他の値と同じようにNULL
に
インデックスを作成したい場合は、NULL
になることはない列をインデックスに追加しましょう。