Oracle SQLチューニング(第30回)「NULL処理構文の理解と効率的なSQLの作成」(6/7)

 「NULL処理構文の理解と効率的なSQLの作成」の6回目は、「IS NOT NULL照会に対する改善方法検索」についてのお話です。それでは、始めましょう。


 SQLチューニングのためには、そのSQLがどのように動いていて、データベースにどのように影響しているのか、を把握する必要があります。『MaxGauge』があれば簡単に状況が把握でき、適切なSQLチューニングができるようになります。

『MaxGauge』の資料はこちらから。


目次[非表示]

  1. 1.7.6 IS NOT NULL照会に対する改善方法検索
    1. 1.1.7.6.1 多様なIS NOT NULL処理とSQL性能問題
      1. 1.1.1.7.6.1.1 CHAR(VARCAHR2)タイプであるカラムのIS NOT NULL
      2. 1.1.2.7.6.1.2 DATEタイプであるカラムのIS NOT NULL
      3. 1.1.3.7.6.1.3 NUMBERタイプであるカラムのIS NOT NULL

7.6 IS NOT NULL照会に対する改善方法検索

 IS NOT NULL照会はSQL構成やカラムのインデックス構成情報により多くの性能偏差が発生しますが、最小限IS NOT NULLで問い合わせるカラムを利用したインデックス スキャンが効率的であるなら、IS NOT NULL照会条件をインデックス スキャンが可能な条件に変更する必要があります。 IS NOT NULL条件を変更する時はカラムのデータ タイプにより変更する方法が変わることになります。 今からインデックスを使えるようにIS NOT NULL条件を変更する方法とIS NOT NULL条件によるSQLの性能問題に対して調べてみることにしましょう。

7.6.1 多様なIS NOT NULL処理とSQL性能問題

    CHAR[VARCHAR2]:column > chr(0)[or column > '']
    DATE:column > to_date('19000101','yyyymmdd')
    NUMBER:column >= 0 OR column < 0

7.6.1.1 CHAR(VARCAHR2)タイプであるカラムのIS NOT NULL

 IS NOT NULLはカラムがNULLでないデータだけ抽出することを言います。 したがってCHARタイプであるカラムに対するIS NOT NULL照会はインデックスを使うことはできません。 したがって効率的なインデックス スキャンになることができるように、文字列のうち一番小さい値より大きい値を問い合わせるように変更するならば、インデックスを使用できない非効率は除去することができるようになります。

[IS NOT NULL 照会]

WITH not_null_t AS (  SELECT 'a' AS c1 FROM DUAL
                      UNION ALL 
                      SELECT null FROM DUAL
                      UNION ALL
                      SELECT 'b' FROM DUAL
                      UNION ALL
                      SELECT '0' FROM DUAL )
SELECT *
  FROM not_null_t
 WHERE c1 IS NOT NULL ;

C1
--
A
B
0


[IS NOT NULLと同じ照会SQL]

WITH not_null_t AS (  SELECT 'A' AS c1 FROM DUAL
                      UNION ALL 
                      SELECT null FROM DUAL
                      UNION ALL
                      SELECT 'B' FROM DUAL
                      UNION ALL
                      SELECT '0' FROM DUAL)
SELECT *
  FROM not_null_t
 --WHERE c1 IS NOT NULL
WHERE c1 > CHR(0);--->または、c1 > ' 'も可能.

C1
--
A
B
0

 CHR関数は数値を引数として受け取り、文字を返す関数で、CHR(0)は文字列のうち最も小さい値を意味します。

7.6.1.2 DATEタイプであるカラムのIS NOT NULL

 IS NOT NULLで照会されるDATEタイプであるカラムをインデックス スキャンが可能な構文に変更する方法は、カラムに存在できない一番小さい値を利用して問い合わせることです。

[IS NOT NULL照会]

ALTER SESSION SET nls_date_format = 'yyyy-mm-dd';

WITH not_null_t AS (  SELECT TO_DATE('20110101','yyyymmdd') AS c1 FROM DUAL
                      UNION ALL 
                      SELECT null FROM DUAL
                      UNION ALL
                      SELECT to_date('20111111','yyyymmdd') FROM DUAL )
SELECT *
  FROM not_null_t
 WHERE c1 IS NOT NULL ;

C1
----------
2011-01-01
2011-11-11


[IS NOT NULLと同じ照会SQL]

ALTER SESSION SET nls_date_format = 'yyyy-mm-dd';

WITH not_null_t AS (   SELECT TO_DATE('20110101','yyyymmdd') as c1 FROM DUAL
                       UNION ALL 
                       SELECT null FROM DUAL
                       UNION ALL
                       SELECT TO_DATE('20111111','yyyymmdd') FROM DUAL)
SELECT *
  FROM not_null_t
WHERE c1 > TO_DATE('19000101','yyyymmdd');---> C1カラムに存在できない以前の値
C1
----------
2011-01-01
2011-11-11

7.6.1.3 NUMBERタイプであるカラムのIS NOT NULL

 NUMBERタイプであるカラムのIS NOT NULL照会は0を基点にしてすべてのデータを抽出するように条件を変更すれば良いのです。

[IS NOT NULL照会]

WITH t1 AS (   SELECT -1 AS c1 FROM DUAL
               UNION ALL 
               SELECT NULL FROM DUAL
               UNION ALL
               SELECT 1 FROM DUAL
               UNION ALL
               SELECT 1.2 FROM DUAL
               UNION ALL
               SELECT 0 FROM DUAL)
SELECT *
FROM t1
WHERE c1 IS NOT NULL ;

           C1
   ----------
        -1
         1
       1.2
         0


[IS NOT NULLと同じ照会SQL]

WITH t1 AS (   SELECT -1 AS c1 FROM DUAL
               UNION ALL
               SELECT NULL FROM DUAL
               UNION ALL
               SELECT 1 FROM DUAL
               UNION ALL
               SELECT 1.2 FROM DUAL
               UNION ALL
               SELECT 0 FROM DUAL )

SELECT *
  FROM t1
 WHERE c1 >= 0 OR c1 < 0 ;

        C1
   ----------
           -1
            1
          1.2
            0

7.6.2 結合処理時IS NOT NULLを活用すること

 テーブル間結合を実行する時、各テーブルのカラムとカラムを比較して処理しますが、NULLを含まない値で比較することになるので、基本的に結合連結カラムはIS NOT NULL条件を有していることになります。

[単一テーブルのカラム比較とIS NOT NULL]

SELECT *
  FROM not_null_t1
 WHERE c3 = c3 ;

--------------------------------------------------------------------------
| Id  | Operation                   | Name               | Rows  | Bytes  |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                    |    66 |   462  |
|   1 |  TABLE ACCESS BY INDEX ROWID| NOT_NULL_T1        |    66 |   462  |
|*  2 |   INDEX FULL SCAN           | NOT_NULL_T1_IDX_01 |    50 |        |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("C3" IS NOT NULL) ---> C3 = C3はC3 IS NOT NULL条件を含む。

[テーブル結合とIS NOT NULL]

SELECT t1.*, t2.*
  FROM not_null_t1 t1,
       not_null_t2 t2
 WHERE t1.c3 IS NOT NULL
   AND t1.c2 = t2.c2
   AND t1.c3 = t2.c3 ;


---------------------------------------------------------------------------
| Id  | Operation                    | Name               | Rows  | Bytes  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                    |    86 |  1204  |
|*  1 |  HASH JOIN                   |                    |    86 |  1204  |
|*  2 |   TABLE ACCESS FULL          | NOT_NULL_T2        |    34 |   238  |
|   3 |   TABLE ACCESS BY INDEX ROWID| NOT_NULL_T1        |    66 |   462  |
|*  4 |    INDEX FULL SCAN           | NOT_NULL_T1_IDX_01 |    50 |        |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T1"."C2"="T2"."C2" AND "T1"."C3"="T2"."C3")
   2 - filter("T2"."C3" IS NOT NULL)
   4 - filter("T1"."C3" IS NOT NULL)

 T2.C3 IS NOT NULLという条件はSQLにはありませんが、t1.c3 = t2.c3条件自体にT1.C3 IS NOT NULLとT2.C3 IS NOT NULL照会条件が含まれています。

 もし、T2.C3列がNULL以外のデータが少ない場合は、T2.C3列に新しい索引を作成し、索引を使用できるように照会条件を変更することで効率的な実行が可能です。

-NOT_NULL_T2.C3に新規インデックスを生成する。

CREATE INDEX not_null_t2_idx_01 ON not_null_t2 (c3) ;

SELECT t1.*, t2.*
  FROM not_null_t1 t1,
       not_null_t2 t2
 WHERE t1.c3 > CHR(0)
   AND t1.c2 = t2.c2
   AND t1.c3 = t2.c3
   AND t2.c3 > CHR(0) ;
---------------------------------------------------------------------------
| Id  | Operation                    | Name               | Rows  | Bytes  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                    |    86  |  1204 |
|*  1 |  HASH JOIN                   |                    |    86  |  1204 |
|   2 |   TABLE ACCESS BY INDEX ROWID| NOT_NULL_T2        |    34  |   238 |
|*  3 |    INDEX RANGE SCAN          | NOT_NULL_T2_IDX_01 |    34  |       |
|   4 |   TABLE ACCESS BY INDEX ROWID| NOT_NULL_T1        |    66  |   462 |
|*  5 |    INDEX RANGE SCAN          | NOT_NULL_T1_IDX_01 |    50  |       |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T1"."C2"="T2"."C2" AND "T1"."C3"="T2"."C3")
   3 - access("T2"."C3">' ')
   5 - access("T1"."C3">' ')



今回は、ここまでとなります。次回は、「'(BLANK)とNULLデータ処理」についてです。それでは、see you next time ^^


 SQLチューニングのためには、そのSQLがどのように動いていて、データベースにどのように影響しているのか、を把握する必要があります。『MaxGauge』があれば簡単に状況が把握でき、適切なSQLチューニングができるようになります。

『MaxGauge』の資料はこちらから。


CONTACT

他社に頼らず自社でデータベースを監視・運用をしませんか?
MaxGaugeがサポートします

お役立ち資料は
こちらから

不明点がある方は、
こちらからお問い合わせください

お電話でのお問い合わせはこちら

平日 10時~18時

人気記事ランキング

タグ一覧