2014-10-07 27 views
0

我們通常會被要求在報表中包含特定級別的層次結構,並且我正在尋找一種方法來加快查詢性能並使用hierarchyid。我做了一些測試,並且我有問題,但我認爲性能會更好。以下是針對給定條目抽取2到4級的示例。我想通過GetAncestor()函數結合hierarchyid的級別來實現這一點。第一個查詢看起來很快,但它被硬編碼爲只返回某一級別的行,以避免使用負值炸燬GetAncestor查詢。第二個樣本解決了這個問題,但速度要慢得多。理想的第二種選擇是我想使用但它不夠快。SQL hierarchyId上的GetAncestor查詢總是會拉動某些級別

--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 

回答

0

在我看來,你應該創建以下指標:

1)

CREATE INDEX IX_hier_rec_name_#_lvl_nodeid 
ON #hier (rec_name) 
INCLUDE (lvl, nodeid) 

滿足查詢#2

select 
    x.rec_name 
    ,x.lvl 
    ,case 
     when x.lvl >=2 then (... = x.nodeid.GetAncestor(x.lvl - 2)) 
     else '*N/A' end as l2 
    ,... 
from #hier x 
where x.rec_name = @recname 

注:查詢#1你可以使用

CREATE INDEX IX_hier_rec_name_lvl_#_nodeid 
ON #hier (rec_name, lvl) 
INCLUDE (nodeid) 

2)你應該

CREATE INDEX IX_hier_nodeID_#_rec_name 
ON #hier (nodeid) 
INCLUDE (rec_name) 

或(更好)與

CREATE UNIQUE INDEX IUN_hier_nodeID_#_rec_name 
ON #hier (nodeid) 
INCLUDE (rec_name) 

如果nodeid值改變這一指數IX_hier_nodeID不允許重複。

+0

這是一個很好的觀點,我應該首先深入研究指標。我也應該創建一個帶有循環和更多行的示例以更好地測試性能。最初,我想也許有一種方法來重新查詢查詢,但也許這只是一個簡單的索引問題。在這個例子中,rec_ID是唯一的,nodeID應該是唯一的(假設我的維護工作正常)。讓我研究一下你使用INCLUDE選項 - 我不熟悉這一點。 – pretzelb 2014-10-08 13:42:52