(普通の) 再帰 CTE を hierarchyid で
SQL Server 2008 に実装されている hierarchyid 型は、経路列挙モデルをベースにした型なので、再帰 CTE を置き換えることができる。
例えば、
SQL Server 2005で再帰クエリ - taediumの日記
の再帰 CTE を hierarchyid で実現してみる。
使用するテーブル
CREATE TABLE [dbo].[Classes]( [id] [hierarchyid] NOT NULL, [name] [nvarchar](50) NOT NULL, CONSTRAINT [PK_Classes] 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]
CREATE TABLE はデザイナ (?) で作ってスクリプト化しただけ。
正直、CREATE TABLE 文とかよく分かりません (何
データの登録
DECLARE @root AS hierarchyid = hierarchyid::GetRoot(); DECLARE @a_collection AS hierarchyid = @root.GetDescendant(NULL, NULL); DECLARE @a_list AS hierarchyid = @a_collection.GetDescendant(NULL, NULL); DECLARE @a_seq_list AS hierarchyid = @a_list.GetDescendant(NULL, NULL); DECLARE @linked_list AS hierarchyid = @a_seq_list.GetDescendant(NULL, NULL); DECLARE @array_list AS hierarchyid = @a_list.GetDescendant(@a_seq_list, NULL); DECLARE @attr_list AS hierarchyid = @array_list.GetDescendant(NULL, NULL); DECLARE @role_list AS hierarchyid = @array_list.GetDescendant(@attr_list, NULL); DECLARE @role_unresolved_list AS hierarchyid = @array_list.GetDescendant(@role_list, NULL); DECLARE @vector AS hierarchyid = @a_list.GetDescendant(@array_list, NULL); DECLARE @stack AS hierarchyid = @vector.GetDescendant(NULL, NULL); INSERT INTO Classes VALUES (@root, 'Object') , (@a_collection, 'AbstractCollection') , (@a_list, 'AbstractList') , (@a_seq_list, 'AbstractSequentialList') , (@linked_list, 'LinkedList') , (@array_list, 'ArrayList') , (@attr_list, 'AttributeList') , (@role_list, 'RoleList') , (@role_unresolved_list, 'RoleUnresolvedList') , (@vector, 'Vector') , (@stack, 'Stack') ;
全部展開して一回の INSERT で済ますこともできる (と思う) けど、超絶に読みにくくなるのでいったん変数に格納しておく。
ちなみにこの状態で
SELECT id , id.ToString() AS id_str , name FROM Class
この SQL を実行すると、
id | id_str | name |
---|---|---|
0x | / | Object |
0x58 | /1/ | AbstractCollection |
0x5AC0 | /1/1/ | AbstractList |
0x5AD6 | /1/1/1/ | AbstractSequentialList |
0x5AD6B0 | /1/1/1/1/ | LinkedList |
0x5ADA | /1/1/2/ | ArrayList |
0x5ADAB0 | /1/1/2/1/ | AttributeList |
0x5ADAD0 | /1/1/2/2/ | RoleList |
0x5ADAF0 | /1/1/2/3/ | RoleUnresolvedList |
0x5ADE | /1/1/3/ | Vector |
0x5ADEB0 | /1/1/3/1/ | Stack |
こうなる。
SELECT
DECLARE @name AS varchar(50) = 'Stack'; DECLARE @str AS varchar(max) = (SELECT id.ToString() FROM Classes WHERE name = @name); SELECT SUBSTRING('+-------------------', 1, Class.id.GetLevel() * 4) + Class.name FROM Classes Class WHERE @str LIKE id.ToString() + '%' ORDER BY id.GetLevel() ;
で、肝心の SELECT 文は再帰 CTE を使ってないから再帰 CTE が分からなくても読める・・・はず!
チョーシに乗って SUBSTRING を使わない版とかも書いてみました。
DECLARE @name AS varchar(50) = 'Stack'; DECLARE @str AS varchar(max) = (SELECT id.ToString() FROM Classes WHERE name = @name); SELECT CASE id.GetLevel() WHEN 0 THEN Class.name ELSE '+' + REPLICATE('-', id.GetLevel() * 4 - 1) + Class.name END FROM Classes Class WHERE @str LIKE id.ToString() + '%' ORDER BY id.GetLevel() ;
hierarchyid 型楽しいですね!
経路列挙モデルをもっと!
おなじみ、リレーショナル・データベースの世界のSQLで木と階層構造のデータを扱う(2)―― 経路列挙モデルをどうぞ。