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 などについても、多数の実績があります。ご相談は無料で承っておりますので、お気軽にお問い合わせください。
コメント