レポートに特定のレベルの階層を含めるように求められることがよくあります。階層 ID を使用してクエリのパフォーマンスを高速化する方法を探しています。私はいくつかのテストを行い、機能するクエリを持っていますが、パフォーマンスは向上する可能性があると思います. 以下は、特定のエントリのレベル 2 から 4 をプルするサンプルです。GetAncestor() 関数を hierarchyid のレベルと組み合わせてこれを行う方法を見つけました。最初のクエリは高速に見えますが、GetAncestor クエリが負の値で爆発するのを避けるために、特定のレベルの行のみを返すようにハード コードされています。2 番目のサンプルはその問題を解決しますが、はるかに遅くなります。理想的には 2 番目のオプションを使用したいと思いますが、十分に高速ではありません。
--drop table #hier
CREATE TABLE #hier
(
rec_ID int NOT NULL,
rec_NAME varchar(6),
nodeID hierarchyid NULL,
lvl AS [nodeid].[GetLevel]() PERSISTED
) ON [PRIMARY]
GO
ALTER TABLE #hier ADD CONSTRAINT
rec_ID PRIMARY KEY CLUSTERED
(
rec_ID
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX IX_hier_nodeID ON #hier
(
nodeID
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE #hier SET (LOCK_ESCALATION = TABLE)
GO
insert into #hier (rec_ID, rec_NAME, nodeID)
SELECT 1, 'CEO', cast('/' as hierarchyid)
union
SELECT 2, 'VP1', cast('/1/' as hierarchyid)
union
SELECT 3, 'VP2', cast('/2/' as hierarchyid)
union
SELECT 4, 'VP3', cast('/3/' as hierarchyid)
union
SELECT 5, 'Mgr1', cast('/1/1/' as hierarchyid)
union
SELECT 6, 'Mgr2', cast('/1/2/' as hierarchyid)
union
SELECT 7, 'Super1', cast('/1/2/1/' as hierarchyid)
union
SELECT 8, 'Ldr1', cast('/1/2/1/1/' as hierarchyid)
union
SELECT 9, 'Work1', cast('/1/2/1/1/1/' as hierarchyid)
union
SELECT 10, 'Work2', cast('/1/2/1/1/2/' as hierarchyid)
union
SELECT 11, 'Work3', cast('/1/2/1/1/3/' as hierarchyid)
GO
-- this runs fast but is hard coded to a level
declare @recname varchar(6)
set @recname = 'Work3'
select
x.rec_name
,x.lvl
,(select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 2) ) as l2
,(select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 3) ) as l3
,(select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 4) ) as l4
from #hier x
where x.rec_name = @recname
and x.lvl >= 4
-- this works for all levels but runs too slow
set @recname = 'Mgr2'
select
x.rec_name
,x.lvl
,case
when x.lvl >=2 then (select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 2) )
else '*N/A' end as l2
,case
when x.lvl >=3 then (select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 3) )
else '*N/A' end as l2
,case
when x.lvl >=4 then (select rec_name from #hier where nodeid = x.nodeid.GetAncestor(x.lvl - 4) )
else '*N/A' end as l2
from #hier x
where x.rec_name = @recname