SQL WITH句で同じSQLを1つのSQLに共通化する
SQL SELECT句での副問合せの基本的な考え方と注意点で紹介したように、副問合せを使用していると同じようなSQL
を複数箇所で行うことがあります。
同じようなSQL
はWITH
句を使用して共通化すればパフォーマンスが改善されることが多いです。
WITH
句によりパフォーマンスが改善されるのはOracle
だけのようです。
(コメント欄にてご指摘いただきました。ありがとうございます。)
例えば以下のようなテーブルとSQL
があるとします。
SQL PARTITION BYの基本と効率的に集計する便利な方法で紹介したように、分析関数を使用すればもっとシンプルに取得できますが、説明の都合であえて以下の方法で取得します。
id | first_name | last_name | department_id | height |
---|---|---|---|---|
1 | 一郎 | 山田 | 1 | 170 |
2 | 次郎 | 佐藤 | 2 | 175 |
3 | 三郎 | 田中 | 1 | 185 |
4 | 四郎 | 鈴木 | 2 | 155 |
SELECT
T1.id,
T1.first_name,
T1.last_name,
T1.department_id,
(
SELECT
AVG(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
) AS avg_height,
(
SELECT
MAX(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
) AS max_height
FROM
employee T1
このSQL
に適当な条件を追加すると以下のようになります。
SELECT
T1.id,
T1.first_name,
T1.last_name,
T1.department_id,
(
SELECT
AVG(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
AND T1.last_name = '山田'
) AS avg_height,
(
SELECT
MAX(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
AND T1.last_name = '山田'
) AS max_height
FROM
employee T1
WHERE
T1.last_name = '山田'
上記SQL
ではemployee
テーブルのSELECT
を何回も行うことになります。
副問合せでemployee
の参照が増えるたびにパフォーマンスはどんどん悪化していきます。
以下のSQL
をVIEW
のように定義しておき、そのテーブルを参照することを考えます。
共通化したいSQL
SELECT *
FROM
employee T1
WHERE
T1.last_name = '山田'
WITH句の使い方
WITH
句を使用すれば1つの副問い合わせをいろいろな場所で使いまわすことができます。
メインのSQL
の前にVIEW
を作成するイメージでWITH
句で副問合せを行います。
WITH employee_with AS (
SELECT *
FROM
employee T1
WHERE
T1.last_name = '山田'
)
SELECT
T1.id,
T1.first_name,
T1.last_name,
T1.department_id,
(
SELECT
AVG(SUB1.height)
FROM
-- WITH句で指定したテーブルを参照
employee_with SUB1
WHERE
T1.department_id = SUB1.department_id
) AS avg_height,
(
SELECT
MAX(SUB1.height)
FROM
-- WITH句で指定したテーブルを参照
employee_with SUB1
WHERE
T1.department_id = SUB1.department_id
) AS max_height
FROM
-- WITH句で指定したテーブルを参照
employee_with T1
複数のテーブルを指定する
WITH
句で複数のテーブル(副問合せ)を使用したい場合はカンマ区切りで指定します。
また、自身の副問合せより前に定義したWITH
句のテーブルを参照できます。
WITH sample_with AS (
SELECT *
FROM sample
WHERE COL1 = 'sample'
),
sample2_with AS (
SELECT *
FROM sample2
-- WITH句で定義したテーブルも参照可能
JOIN sample_with
ON sample2.COL1 = sample_with.COL1
WHERE sample2.COL1 = 'sample'
)
SELECT