質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Oracle Database

Oracle Databaseは、米オラクルが開発・販売を行うリレーショナルデータベース管理システムです。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

パフォーマンス

コード効率の向上や計算に関する質問には、このタグを使ってください。

Q&A

1回答

1161閲覧

【Oracle19C】記載のSQLが遅くなる理屈がわからない

ImPathy

総合スコア29

Oracle Database

Oracle Databaseは、米オラクルが開発・販売を行うリレーショナルデータベース管理システムです。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

パフォーマンス

コード効率の向上や計算に関する質問には、このタグを使ってください。

1グッド

1クリップ

投稿2023/11/30 04:30

編集2023/11/30 05:05

はじめに

下部に記したようなSQLがあります。そのままはコピペできないため、テーブル名等を加工しています。
これを2つの環境(以下、テスト環境・本番環境とする)で実行したときに、本番環境で実行した場合にのみ想定より時間がかかってしまうのですがその理屈がわからず、お力添えいただきたく存じます。

SQL

1SELECT 2 SUB.SEQ_NO 3 ,SUB.ID 4 ,SUB.PROCESS_DATE 5 ,SUB.IMAGE_KEY 6FROM 7 ( 8 SELECT 9 /* 行番号を振る*/ 10 ROW_NUMBER() OVER (ORDER BY MAIN.ID) AS SEQ_NO 11 ,MAIN.ID AS ID 12 ,MAIN.PROCESS_DATE AS PROCESS_DATE 13 ,CASE WHEN MAIN.NO_IMAGE = '1' THEN '' 14 WHEN MI.IMAGE_DATA IS NULL AND SI.IMAGE_DATA IS NULL AND BI.IMAGE_DATA IS NULL THEN '' 15 ELSE MAIN.ID ||MAIN.SEQUENCE || PROCESS_DATE END AS IMAGE_KEY 16 17 FROM 18 MAIN_TABLE MAIN 19 20 LEFT OUTER JOIN MAIN_IMAGE MI 21 ON MAIN.ID = MI.ID 22 LEFT OUTER JOIN SUB_IMAGE SI 23 ON MAIN.ID = SI.ID 24 LEFT OUTER JOIN BACKUP_IMAGE BI 25 ON MAIN.ID = BI.ID 26 )SUB 27WHERE 28 SUB.SEQ_NO <= 100000

調べたこと

1. 実行計画
両環境において、全く同じSQLの実行計画を取得しました。すると、トータルコストに大きな差が出ました。SQLを加工している関係上実行計画をそのまま貼付する訳にもいかないのですが、具体的に記載しますと本番環境の方はソート対象行数がおおよそ6000億行ぐらいにまで膨れ上がっており、そこにかなりコストおよび時間がかかっているようでした。検証環境の実行計画は、ソート対象行数は10万行となっており想定通りでした。つまり、テーブルのレコード数に起因することがここでわかるかと思います。

2. SQL本体
実行計画から「どの記述が影響しているか」を特定できなかったため、SQLの結合条件やSELECT句などをコメントアウトしながら本番環境で都度実行し、「どの部分がコメントアウトされた時点でコストが増えるのか」という調査をしました。すると、下部に記載したSQLのうち、15行目の以下部分をコメントアウトするとコストは下がり、実行計画上のソート対象行数も10万行になりました。

AND BI.IMAGE_DATA IS NULL

確かに検証環境と比較すると、BACKUP_IMAGEには大量のレコードが登録されていましたので(検証環境:約100件、本番環境:約1500万件)、そこの件数が影響していることもここでわかるかと思います。

また、BACKUP_IMAGEテーブルは現状登録されているデータの件数から増減が一切ないため、統計情報の更新は行われていません。数年前に1500万件を一気に登録した時点で統計情報を取り、それ以降はデータの増減がないようです。ですのでインデックスが効いていない、というわけでもないかと思います。

不明点

・テーブル同士を結合しただけでは特にコストが増えないのに、CASE文の中で結合先のテーブルカラムを参照するとコストが爆増する理屈がわからない。実行計画を見るに、ソート対象行数が増えているため、たとえば結合条件が間違っていて取得行が増殖するようなSQLになっているとかなら納得はできるが、今回登場するMAINテーブルとBACKUP_IMAGEテーブルの関係は1対1なのでそれも起きえないと考えている。

補足情報

・MAIN_IMAGE、SUB_IMAGE、BACKUP_IMAGEのいずれも、IMAGE_DATAカラムに画像ファイルをBLOB形式で保持しています(平均100KBほど)。
・本文に記載済みですが、MAINと各種IMAGEについては1対1になります。

MAINテーブル

IDPROCESS_DATE
00120231123

MAIN_IMAGE/SUB_IMAGE/BACKUPテーブル

IDIMAGE_DATA
001(BLOB)

長文になりましたが、ご助力願えればと思います。

宜しくお願い致します。

退会済みユーザーを押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOZ6.0

2023/11/30 11:27 編集

SQLもフェイク、実行計画も載せないではどうしようもない気がしますが・・・ 駆動表は MAIN_TABLE になっていますか?
ikedas

2023/11/30 11:45

テスト環境・本番環境各々の、今回のクエリに関係する各テーブルのレコード数を一覧で示してください。 念のたためですがこのコメント欄に書くのではなく質問文を編集して書いてください。
guest

回答1

0

テーブル同士を結合しただけでは特にコストが増えないのに、CASE文の中で結合先のテーブルカラムを参照するとコストが爆増する理屈がわからない。

結合後に行われる参照の際に、その参照項目が、使用したインデックスに含まれなければ実テーブルを参照する事になります。

ですので、質問の内容であれば、インデックスのみのスキャンとなるように、以下のインデックスを追加してみて下さい。

BACKUP_IMAGE (ID, IMAGE_DATA)

※追加したインデックスが使用されないようであれば、ヒント文を使うか、SQLの記述の書き換えを行って下さい

SQLのうち、15行目の以下部分をコメントアウトするとコストは下がり、実行計画上のソート対象行数も10万行になりました。

対象行数までコントロールする場合は、上記以外のテーブルに同様にインデックスを追加して結合条件を以下の様にしてみて下さい。

SQL

1 LEFT OUTER JOIN MAIN_IMAGE MI 2 ON MAIN.ID = MI.ID AND MI.IMAGE_DATE IS NOT NULL 3 LEFT OUTER JOIN SUB_IMAGE SI 4 ON MAIN.ID = SI.ID AND SI.IMAGE_DATE IS NOT NULL 5 LEFT OUTER JOIN BACKUP_IMAGE BI 6 ON MAIN.ID = BI.ID AND BI.IMAGE_DATE IS NOT NULL

もう一つのアプローチとしては、インデックスに無い参照を止める事です。
具体的にはIMAGE_KEYの編集条件を変える事です。

WHEN MI.IMAGE_DATA IS NULL AND SI.IMAGE_DATA IS NULL AND BI.IMAGE_DATA IS NULL

上記条件がイメージデータが無い事を表しているなら、以下に書き換えると、現状のインデックスのみで解決する事になります。

WHEN MI.ID IS NULL AND SI.ID IS NULL AND BI.ID IS NULL

見返してみたところ、最初に抽出する件数を絞り込めば良さそうなので(MAINとIMAGEの各テーブルが1:1という前提)、以下のSQLを試してみて下さい。
※上記に加え、インデックスを追加するか、インデックスに無い項目を参照しないようにすると更に高速になると思います

SQL

1SELECT 2 SUB.SEQ_NO 3 ,MAIN.ID 4 ,MAIN.PROCESS_DATE 5 ,CASE WHEN MAIN.NO_IMAGE = '1' THEN '' 6 WHEN MI.IMAGE_DATA IS NULL AND SI.IMAGE_DATA IS NULL AND BI.IMAGE_DATA IS NULL THEN '' 7 ELSE MAIN.ID ||MAIN.SEQUENCE || PROCESS_DATE END AS IMAGE_KEY 8FROM MAIN_TABLE MAIN 9 inner join ( 10 select ID , ROW_NUMBER() OVER (ORDER BY ID) AS SEQ_NO 11 from MAIN_TABLE 12 ) sub 13 on MAIN.ID=sub.ID and sub.seq_no<= 100000 14 LEFT OUTER JOIN MAIN_IMAGE MI 15 ON MAIN.ID = MI.ID 16 LEFT OUTER JOIN SUB_IMAGE SI 17 ON MAIN.ID = SI.ID 18 LEFT OUTER JOIN BACKUP_IMAGE BI 19 ON MAIN.ID = BI.ID

※NO_IMAGE = '1'になっていないがバグなのであれば、データを補正すると共に、バグ修正する方が良いのではないでしょうか。

投稿2023/12/01 00:25

編集2023/12/01 08:34
sazi

総合スコア25195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問