WITH句を使って一時的なビューの作成と利用、再帰的処理の呼び出しなど
既存データに対して何らかの変換が必要になってデータコンバートを行おうとすると、時々、システムとしては不要だけど、データコンバートの時だけビューが欲しいといった場合があります。
この場合、データコンバートの時だけ必要なビューをわざわざCREATEを使って作成したくないのが普通で、作成してしまった場合、使用後に作成したビューを削除し忘れたりすることがあり、不要なビューがそのまま残ってしまうと、場合によっては管理が煩雑になりますし、プロジェクトを離れることになったら、引継ぎした担当者から文句を言われたりすることがあります。
そういった事態を避けるためにも、Oracleでは一時的にビューを利用したい場合、WITHという便利なものがあるので、これを使います。
WITHを使ったSQLの例
以下はWITHを使ったSQLの例になります。
WITH
ORDERS AS (
SELECT 1 AS ID, '注文A' AS NAME FROM DUAL
UNION SELECT 2 AS ID, '注文B' AS NAME FROM DUAL
),
ORDER_PRODUCTS AS (
SELECT 1 AS ORDER_ID, 1 AS ID, '商品A' AS NAME, 100 AS PRICE FROM DUAL
UNION SELECT 1 AS ORDER_ID, 2 AS ID, '商品A' AS NAME, 200 AS PRICE FROM DUAL
UNION SELECT 1 AS ORDER_ID, 3 AS ID, '商品A' AS NAME, 300 AS PRICE FROM DUAL
)
SELECT
o.ID AS O1_ID,
op1.ID AS P1_ID, op1.NAME AS P1_NAME, op1.PRICE AS P1_PRICE,
op2.ID AS P2_ID, op2.NAME AS P2_NAME, op2.PRICE AS P2_PRICE,
op3.ID AS P3_ID, op3.NAME AS P2_NAME, op3.PRICE AS P3_PRICE
FROM ORDERS o
LEFT JOIN ORDER_PRODUCTS op1 ON op1.ORDER_ID = o.ID AND op1.ID = 1
LEFT JOIN ORDER_PRODUCTS op2 ON op2.ORDER_ID = o.ID AND op2.ID = 2
LEFT JOIN ORDER_PRODUCTS op3 ON op3.ORDER_ID = o.ID AND op3.ID = 3
WHERE o.ID = 1;
「ORDERS」が注文データ、「ORDER_PRODUCTS」が注文された商品のデータという位置付けで、上記のSQLは、注文Aの商品データを商品ごとに列にして、1行で取得する内容となっています。
ちなみに、このSQLを実行すると以下のような結果になります。
仕事で利用した際は、データコンバート用に1000行近いPL/SQLを作成しなければならなくなり、テーブルから何度も同じサブクエリでデータを取得しないといけなかったので、このWITHは非常に便利でした。
WITHを使った再帰的な呼び出し
上記のSQLではWITHを単純なビューとして利用しましたが、WITHを使う要件としては、SQLで再帰的な処理をしたいといった理由が多いと思います。
以下にWITHで再帰的な呼び出しを行う簡単な例を示します。
WITH
CATEGORIES AS (
SELECT 1 AS ID, 0 AS PARENT_ID, 'A' AS NAME FROM DUAL
UNION SELECT 11 AS ID, 1 AS PARENT_ID, 'A1' AS NAME FROM DUAL
UNION SELECT 12 AS ID, 1 AS PARENT_ID, 'A2' AS NAME FROM DUAL
UNION SELECT 121 AS ID, 12 AS PARENT_ID, 'A21' AS NAME FROM DUAL
UNION SELECT 13 AS ID, 1 AS PARENT_ID, 'A3' AS NAME FROM DUAL
UNION SELECT 131 AS ID, 13 AS PARENT_ID, 'A31' AS NAME FROM DUAL
UNION SELECT 132 AS ID, 13 AS PARENT_ID, 'A32' AS NAME FROM DUAL
UNION SELECT 2 AS ID, 0 AS PARENT_ID, 'B' AS NAME FROM DUAL
UNION SELECT 21 AS ID, 2 AS PARENT_ID, 'B1' AS NAME FROM DUAL
UNION SELECT 211 AS ID, 21 AS PARENT_ID, 'B11' AS NAME FROM DUAL
UNION SELECT 212 AS ID, 21 AS PARENT_ID, 'B12' AS NAME FROM DUAL
),
CATEGORY_LEVEL(id, parent_id, name, lvl) AS (
SELECT c.ID, c.PARENT_ID, c.NAME, 1 AS LVL
FROM CATEGORIES c WHERE PARENT_ID = '0'
UNION ALL
SELECT c.ID, c.PARENT_ID, c.NAME, lvl + 1 AS LVL
FROM CATEGORIES c, CATEGORY_LEVEL ct
WHERE c.PARENT_ID = ct.ID
)
SELECT *
FROM CATEGORY_LEVEL;
上記のSQLでは、WITHで定義したCATEGORY_LEVELを呼び出すと、CATEGORIESのデータを再帰的に取得して、取得したデータに階層レベル(LVL)の情報を付与して結果データを返してくれます。
実行結果は以下の通り。
パッと見は理解しにくいですが、使いどころが時々あるので、こういうことができるんだというのを知っておくといいと思います。