SQLのwith句についてまとめています。
目次
SQLのwithをSELECT文で使うサンプル

with句を使うと、入り組んだサブクエリを単純なクエリに分解して、スッキリ記述できるというメリットがあります。
【関連記事】
▶SQLのWITH句で可読性アップ!再帰共通表式でサクッとSQL文を書く
with句はクエリ実行中のみ有効な一時テーブルを作ります。サブクエリ結果に名前をつけて、メインクエリで参照することができます。なお、with句を使った副問合せの定義は、共通テーブル式(CTE)と呼ばれます。
特にサブクエリを同時に2つ以上使用するような複雑なクエリで威力を発揮します。
各DBMSのSELECT文でwith句を使う例を紹介します。
Oracleのwithの構文
WITH query_name ([c_alias [, c_alias]...]) AS (subquery) [search__clause] [cycle_clause] [, query_name ([c_alias [, c_alias]...]) AS (subquery) [search_clause] [cycle_clause]]...
query_nameは、副問合せブロックにつける名前です。副問合せが複数存在する場合に、query_nameを指定して参照するんですね。副問合せの名前はインライン・ビューまたは一時表とし扱うことが可能です。
以下は、部署のコストをdept_costs、部署のコストの平均をavg_costと副問合せに名前をつけ、平均よりもコストが多い部署名を抽出する例です。
WITH
dept_costs AS (
SELECT department_name, SUM(salary) dept_total
FROM employees e, departments d
WHERE e.department_id = d.department_id
GROUP BY department_name),
avg_cost AS (
SELECT SUM(dept_total)/COUNT(*) avg
FROM dept_costs)
SELECT * FROM dept_costs
WHERE dept_total >
(SELECT avg FROM avg_cost)
ORDER BY department_name;
DEPARTMENT_NAME DEPT_TOTAL
------------------------------ ----------
Sales 304500
Shipping 156400
SQL Serverのwithの構文
[ WITH <common_table_expression> [ ,...n ] ]
<common_table_expression>::=
expression_name [ ( column_name [ ,...n ] ) ]
AS
( CTE_query_definition )
参考)WITH common_table_expression (Transact-SQL) – SQL Server | Microsoft Docs
SQL Serverはwith句で複数の副問合せの指定が可能です。副問合せごとに名前(common_table_expression)をつけ、カラム名の指定も可能です。
以下は、販売員(SalesPersonID)ごとの年間の販売注文数の合計を出力する例です。
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
MySQLのwithの構文
with_clause:
WITH [RECURSIVE]
cte_name [(col_name [, col_name] ...)] AS (subquery)
[, cte_name [(col_name [, col_name] ...)] AS (subquery)] ...
共通テーブル式(副問合せブロック)には、名前がつけられます。上記の構文ではcte_nameです。複数の共通テーブル式の定義が可能で、別の共通テーブル式の参照が可能です。
以下は、table1とtable2のselect文で定義した2つの共通テーブル式cte1とcte2を結合する例です。
WITH cte1 AS (SELECT a, b FROM table1), cte2 AS (SELECT c, d FROM table2) SELECT b, d FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;
PostgreSQLのwithの構文
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
参考)Postgresql公式ドキュメント:WITH問い合わせ(共通テーブル式)
上記はPostgreSQLでwithを使ったクエリの例です。サブクエリにてregional_salesとtop_regiionsという2つの共通テーブル式を定義し、地域ごとの製品と売上数量、売上金額を抽出しています。
withを使って副問合せを記述すると、複雑な問い合わせを単純なクエリに分解することができる点です。
また、withを使って再帰的問い合わせの記述も可能です。
WITH RECURSIVE included_parts(sub_part, part, quantity) AS (
SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'
UNION ALL
SELECT p.sub_part, p.part, p.quantity
FROM included_parts pr, parts p
WHERE p.part = pr.sub_part
)
SELECT sub_part, SUM(quantity) as total_quantity
FROM included_parts
GROUP BY sub_part
上記は部品(part)と構成部品(sub_part)を再帰的に問い合わせ、製品すべてのの部品、構成部品を抽出する例です。
SQLのwith句のまとめ


- with句で共通テーブル式を定義して、複雑なサブクエリを単純なクエリに分解して記述可能
- with句では、複数の共通テーブル式を記述可能
- with句で、再帰的問い合わせを記述できる