SQLServerで共通テーブル式(CTE)を利用して再帰呼び出しする

ブログ

SQLServerで、自分のテーブルに紐づくデータを再帰的に呼び出したい場合があります。

その場合は、共通テーブル式(CTE) を利用することで、再帰的にデータを取得できます。

1.再帰的な呼び出し

Webページのパンくずリスト(いまどこにいるかの道標)を考えると想像しやすいかもしれません。

例えば、下記のようなデータの構成の場合です。



これを実現するテーブル「パンくずリストテーブル」は、ページの情報と、親となるページのIDを持った構成となります。

【パンくずリストテーブル】
ID
ページのID (例:page-01)
名前
ページ名称 (例:会社概要)
URL
ページのURL (例:http://goodtech.co.jp/company/)
親ID
親となるID (例:page-0)

こういった場合で、「親ID」をたどって全てのページの一覧を取得する方法が、「再帰的な呼び出し」と言います。

2.共通テーブル式(CTE)

SQL Server で、再帰的な呼び出しでデータを取得したい時に利用するのが、共通テーブル式(CTE)です。

共通テーブル式(CTE)は、下記のようなものになります。

  • SQL文を実行した時のみに作成できる一時的なテーブル
  • 自己参照(再帰的な呼び出し)が可能
  • 結果を複数回参照できる

詳細は、Microsoft SQL Server 「共通テーブル式の使用」を参照ください。

3.テストデータの用意

まずは、パンくずリストテーブルを作成します。

-- パンくずリストテーブル作成
CREATE TABLE Breadcrumbs(
	Id varchar(10) NOT NULL,
	Name nvarchar(200) NOT NULL,
	Url nvarchar(1000) NOT NULL,
	ParentId varchar(10),
CONSTRAINT [PK_Breadcrumbs] PRIMARY KEY CLUSTERED
(
	Id ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

次に、テストデータを登録します。

一番上のデータとなる「GoodTechトップページ」は、親ID(ParentId)を「NULL」にすることがポイントです。

-- パンくずリストデータ作成
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-0', 'GoodTechトップページ', 'http://goodtech.co.jp/', NULL)
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-01', '会社概要', 'http://goodtech.co.jp/company-profile/', 'page-0')
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-02', '提供サービス', 'http://goodtech.co.jp/services/', 'page-0')
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-03', 'お知らせ', 'http://goodtech.co.jp/notices/', 'page-0')
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-0301', 'ホームページを公開しました', 'http://goodtech.co.jp/notices/greeting2/', 'page-03')
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-04', 'お問い合わせ', 'http://goodtech.co.jp/contact/', 'page-0')
INSERT INTO Breadcrumbs (Id, Name, Url, ParentId)VALUES('page-06', 'サイトマップ', 'http://goodtech.co.jp/sitemap/', 'page-0')

データを全件取得すると、下記のようになります。

4.再帰的なデータの取得

下記SQLで、データを再帰的に取得できます。

-- 共通テーブル式(CTE)を利用して再帰的にデータを取得する。
declare @Id varchar(10) = 'page-0';

WITH Breadcrumbs_CTE AS
(
	SELECT
		b1.*,
		CAST(b1.Id AS nvarchar(4000)) AS IdPath,
		CAST(b1.Name AS nvarchar(4000)) AS NamePath
	FROM
		Breadcrumbs b1
	WHERE
		b1.Id = @Id
	UNION ALL
	SELECT
		b2.*,
		Breadcrumbs_CTE.IdPath + ' - ' + b2.Id AS IdPath,
		Breadcrumbs_CTE.NamePath + ' > ' + b2.Name AS NamePath
	FROM
		Breadcrumbs b2
		INNER JOIN Breadcrumbs_CTE
			ON
			b2.ParentId = Breadcrumbs_CTE.Id
)

SELECT
	cte.IdPath,
	cte.NamePath,
	cte.Url,
	cte.Id,
	cte.ParentId
FROM
	Breadcrumbs_CTE cte
ORDER BY
	cte.IdPath

構文は、
WITH [一時テーブル名] AS ( [取得SQL] )
SELECT [列名] FROM [一時テーブル名]
となります。

WITH句のカッコ内で、データを取得します。
その際に、WITH句で指定した一時テーブル名を呼び出すことで、再帰的な呼び出しが可能になります。

WITH句の後のSELECT文は、一時テーブルの内容を表示するための式となります。

いかがでしたか?

試してみると、簡単で便利なものだということがわかります。

グッドテックでは、SQL Server を利用したシステム開発やチューニング、また、Azure SQL Database などについても、多数の実績があります。ご相談は無料で承っておりますので、お気軽にお問い合わせください。

[get_formcontact target=””]

コメント