L
o
a
d
i
n
g
.
.
.

ホーム

お知らせ

製品・ソリューション

サービス

導入事例・パートナー

EXEM Academy・ブログ

会社情報

採用情報

2021.07.07

SQLチューニング(第9回)「サブクエリ動作方式を理解すること」(7/8)

今回は、結合とサブクエリに関する考察です。早速、始めましょう。


2.1.3.2. 結合の代わりにサブクエリを活用しよう

 リゾルバの役割を実行するテーブルとは、From句に記載され、テーブルのSelect句に抽出するカラムはなく、単にWhere句に結合条件やFilter条件でのみ使用されているテーブルをいう。

 単にリゾルバとしての役割のみを実行するテーブルに対して結合により処理するとき、多くの場合、予期しない非効率が発生することがある。リゾルバの役割のテーブルを結合で実行するように処理するときに発生するパフォーマンスの問題とその解決方法について調べる。理解を助けるために、以下のSQLを用いて説明する。

var b1 varchar2(10)
var b2 number
var b3 number
exec :b1 := 'A'
exec :b2 := 200000
exec :b3 := 300000

SELECT /*+ LEADING(T2 T1 T3) USE_NL(T2 T1 T3) */
       DISTINCT t2.c1, t2.c2, t3.c3
  FROM SUBQUERY_T2 T2,
       SUBQUERY_T1 T1,
       SUBQUERY_T3 T3 
 WHERE t2.c2 = :b1
   AND t2.c1 >= :b2 
   AND t2.c1 <= :b3
   AND t2.c1 = t1.c4
   AND t2.c2 = t1.c5
   AND t1.c4 = t3.c1
   AND t1.c5 = t3.c2

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.01       0.04          0          0          0           0
Fetch      130      1.06       7.25       1893     254597          0        1923
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      132      1.07       7.29       1893     254597          0        1923

Rows     Row Source OPERATION
-------  ---------------------------------------------------
   1923  HASH UNIQUE (cr=254597 pr=1893 pw=0 time=7250803 us)
 123072   FILTER  (cr=254597 pr=1893 pw=0 time=282167079 us)
123072 TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=254597 pr=1893 …)
246145 NESTED LOOPS (cr=254479 pr=1775 …)
123072 NESTED LOOPS (cr=8013 pr=1646 …)
   3846       INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=12 pr=12 …)
123072 INDEX RANGE SCAN SUBQUERY_T1_IDX_01 (cr=8001 pr=1634 …)
123072 INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=246466 pr=129 …)

SQLおよび実行結果整理

  • 結合順序:T2→ T1→ T3 (結合は固定、T2とT3の結合連結条件なし)
  • データ抽出:t2.c1,t2.c2,t3.c3
  • 条件節:WHERE t2.c2 =:b1 AND t2.c1 >=:b2 AND t2.c1 <=:b3 AND t2.c1 = t1.c4 AND t2.c2 = t1.c5 AND t1.c4 = t3.c1 AND t1.c5 = t3.c2
Key Point : T2のC1カラムはUniqueであり、T1のC4カラムはUniqueではない。 そしてテーブルT3のC1カラムとC3カラム値がそれぞれUniqueである。

 SQLトレース結果を見れば、まずT2テーブルを先に読んで3,846件抽出する。 そしてT1テーブルと1:M結合を実行し、データが123,072件に大きく増加した。 抽出されたデータ123,072件は再びT3テーブルと1:1結合を実行して合計123,072件抽出された。 最終抽出された123,072件をDISTINCT処理して重複値を除去し、1,923件と大きく減少された。すなわち、T1テーブルと結合処理時不必要なデータが増加したと判断することができる。

 したがってT2テーブルとT3テーブルを先に結合した後に最後にT1テーブルと結合するように結合順序を変えるならば、性能が改善されると予想される。 だが、T2とT3テーブル間には直接的な結合条件がなくて、結合順序を変更できないのでこの方法は使うことはできない。

 結合順序を変更できないので、これ以上性能改善方法がなく見える。 だが、改善方法はある。もし、初めから重複値を抽出しないならば、結合連結の試行回数が減って非効率が改善される。

 まず、T2とT1テーブルを結合すると、データが急激に増加したことから見てT1との結合時に重複データが多く発生したものと予想される。そして実際にデータを分析してみると、T1との結合時に、重複したデータが多く発生することを確認することができる。幸いなことにT1テーブルの場合、データは抽出せず、結合接続のみに関与するリゾルバの役割をするテーブルである。つまり、値を抽出せずに、単純にデータの存在の有無を確認する目的で使用されたテーブルであるため、EXISTSを使用するサブクエリで置き換えても、データの整合性を損なうことはない。従って次のようにEXISTSを使用したサブクエリにSQLを変更して実行すればよい。

Note.ただ、SUMやCOUNTなどのグループ関数を実行する場合はデータが毀損されることがあるので、データ整合性チェックは必ず実行しなければならない。 前の場合はデータ整合性き損が発生しない場合である。
SELECT /*+ LEADING(T2 T1@SUB T3) USE_NL(T2 T1@SUB T3) */
       t2.c1, t2.c2, t3.c3 
  FROM SUBQUERY_T2 T2,
       SUBQUERY_T3 T3
 WHERE t2.c2 = :b1
   AND t2.c1 >= :b2 
   AND t2.c1 <= :b3
   AND EXISTS (  SELECT /*+ QB_NAME(SUB) */
                       'x'
                  FROM SUBQUERY_T1 T1
                 WHERE t1.c4 = t2.c1
                   AND t1.c5 = t2.c2
                   AND t1.c4 = t3.c1                   
AND t1.c5 = t3.c2 )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch      130      0.06       0.10          0      12616          0        1923
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      132      0.06       0.10          0      12616          0        1923

Rows     Row Source OPERATION
-------  ---------------------------------------------------
   1923  FILTER  (cr=12616 pr=0 pw=0 time=94301 us)
   1923 TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=12616 pr=0 …)
   3847    NESTED LOOPS  (cr=12378 pr=0 pw=0 time=15396 us)
   1923     NESTED LOOPS  (cr=8269 pr=0 pw=0 time=88503 us)
   3846 INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=140 pr=0 …)
   1923      SORT UNIQUE (cr=8129 pr=0 pw=0 time=88914 us)
123072 INDEX RANGE SCAN SUBQUERY_T1_IDX_01 (cr=8129 pr=0 …)
   1923 INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=4109 pr=0 …)

 トレース結果を見れば、T2テーブルで抽出された3,846件に対してT1との結合の部分をEXISTSに変更した結果抽出されたデータが1,923件と大幅に減った。したがってT3テーブルの結合連結も123,072回から1,923回に大幅減って性能が大幅に改善された。

 ここまでリゾルバの役割を実行するテーブルが結合で処理されて発生するパフォーマンスの問題の原因と改善方法について調べてみた。同様の型のSQLを作成するときに、SQLの性格を綿密に把握することはもちろん、サブクエリの特性をうまく活用して、最も効率的な方法でSQLを作成する必要がある。


 リゾルバテーブルの話いかがでしたか?次回は、「サブクエリ動作方式を理解すること」の最終回として、Where節のサブクエリのお話をお届けします。See You ^-^


PHP Code Snippets Powered By : XYZScripts.com