2017-04-08 40 views
0

我有一張表,每行記錄都與其他行記錄相關,有些行也與其他行無關。如何使用SQL Server中的某些條件將行轉換爲列的基礎?

我的記錄是這樣的

項目

ID   ItemLookupCode  UnitOfMeasure  ParentItem 
-------------------------------------------------------------- 
111   100006C0005   CRT     0 
112   100006B0001   BAG    111  // this row is child of ID 111 

221   100027C0002   CRT     0 
222   100027T0012   PCT    221 
223   100027P0001   PC     222 

期待輸出

ItemRelation

我想這下面的查詢,它的正常工作。是否有任何其他更好的解決方案的性能:

SELECT DISTINCT 
    TOP (100) PERCENT dbo.Item.ID, 
    dbo.Item.ItemLookupCode, 
    dbo.Item.UnitOfMeasure, 
    Item_1.ID AS ChildID1, 
    Item_1.ItemLookupCode AS ChildItemLookupCode1, 
    Item_1.ParentItem AS ChildParentItem1, 
    Item_1.UnitOfMeasure AS ChildUOM1, 
    Item_2.ID AS ChildID2, 
    Item_2.ItemLookupCode AS ChildItemLookupCode2, 
    Item_2.UnitOfMeasure AS ChildUOM2, 
    Item_3.ID AS ChildID3, 
    Item_3.ItemLookupCode AS ChildItemLookupCode, 
    Item_3.UnitOfMeasure AS ChildUOM3 
FROM   
    dbo.Item 
LEFT OUTER JOIN 
    dbo.Item AS Item_1 ON dbo.Item.ID = Item_1.ParentItem 
LEFT OUTER JOIN 
    dbo.Item AS Item_2 ON Item_1.ID = Item_2.ParentItem 
LEFT OUTER JOIN 
    dbo.Item AS Item_3 ON Item_2.ID = Item_3.ParentItem 
+0

使用遞歸查詢(CTE)。請參閱https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx –

回答

1

我的猜測是ParentItem沒有索引。 並且在該字段中有3個連接,全表掃描使其變慢。
但ID可能是主鍵,所以這是索引。

如果ParentItem添加索引是不是一種選擇?
然後,你可以通過一個臨時表與父母的索引。

CREATE TABLE #tmpItem (ParentID int, ID int); 

INSERT INTO #tmpItem (ParentID, ID) 
SELECT ParentItem, ID 
FROM dbo.Item; 

CREATE CLUSTERED INDEX #IDX_C_tmpItem ON #tmpItem(ParentID); 

SELECT --TOP (100) PERCENT 
Item_0.ID AS ID, 
Item_0.ItemLookupCode AS ItemLookupCode, 
Item_0.UnitOfMeasure AS UnitOfMeasure, 
Item_1.ID AS ChildID1, 
Item_1.ItemLookupCode AS ChildItemLookupCode1, 
Item_1.ParentItem AS ChildParentItem1, 
Item_1.UnitOfMeasure AS ChildUOM1, 
Item_2.ID AS ChildID2, 
Item_2.ItemLookupCode AS ChildItemLookupCode2, 
Item_2.ParentItem AS ChildParentItem2, 
Item_2.UnitOfMeasure AS ChildUOM2, 
Item_3.ID AS ChildID3, 
Item_3.ItemLookupCode AS ChildItemLookupCode3, 
Item_3.ParentItem AS ChildParentItem3, 
Item_3.UnitOfMeasure AS ChildUOM3 
FROM (
    SELECT I0.ID as ID0, I1.ID as ID1, I2.ID as ID2, I3.ID as ID3 
    FROM #tmpItem AS I0 
    LEFT JOIN #tmpItem AS I1 ON (I0.ID = I1.ParentID) 
    LEFT JOIN #tmpItem AS I2 ON (I1.ID = I2.ParentID) 
    LEFT JOIN #tmpItem AS I3 ON (I2.ID = I3.ParentID) 
) Q 
LEFT JOIN dbo.Item Item_0 ON Q.ID0 = Item_0.ID 
LEFT JOIN dbo.Item Item_1 ON Q.ID1 = Item_1.ID 
LEFT JOIN dbo.Item Item_2 ON Q.ID2 = Item_2.ID 
LEFT JOIN dbo.Item Item_3 ON Q.ID3 = Item_3.ID; 

下面只是一個使用遞歸查詢的實驗。
使用ID上的索引。
是的,我知道,它沒有返回沒有孩子的父母。請不要判斷。

declare @Item table (ID int primary key, ItemLookupCode varchar(11), UnitOfMeasure varchar(3), ParentItem int); 
insert into @Item (ID, ItemLookupCode, UnitOfMeasure, ParentItem) values 
(111,'100006C0005','CRT',0), (112,'100006B0001','BAG',111), 
(221,'100027C0002','CRT',0), (222,'100027T0012','PCT',221), (223,'100027P0001','PC',222), 
(224,'100027X0001','XX',223),(225,'100027Y0001','YY',223), 
(226,'100027Z0001','ZZ',225); 

WITH RCTE AS 
(
    select ID as StartID, 0 as PrevID, 0 as Level, ID, ParentItem as ParentID, ItemLookupCode, UnitOfMeasure 
    from @Item 

    union all 

    select RCTE.StartID, RCTE.ID, RCTE.Level + 1, t.ID, t.ParentItem, t.ItemLookupCode, t.UnitOfMeasure 
    from RCTE 
    join @Item t on (RCTE.ParentID = t.ID) 
) 
select 
max(case when ReverseLeveL = 0 then ID end) as ID0, 
max(case when ReverseLeveL = 0 then ItemLookupCode end) as ItemLookupCode0, 
max(case when ReverseLeveL = 0 then UnitOfMeasure end) as UnitOfMeasure0, 
max(case when ReverseLeveL = 1 then ID end) as ID1, 
max(case when ReverseLeveL = 1 then ItemLookupCode end) as ItemLookupCode1, 
max(case when ReverseLeveL = 1 then UnitOfMeasure end) as UnitOfMeasure1, 
max(case when ReverseLeveL = 2 then ID end) as ID2, 
max(case when ReverseLeveL = 2 then ItemLookupCode end) as ItemLookupCode2, 
max(case when ReverseLeveL = 2 then UnitOfMeasure end) as UnitOfMeasure2, 
max(case when ReverseLeveL = 3 then ID end) as ID3, 
max(case when ReverseLeveL = 3 then ItemLookupCode end) as ItemLookupCode3, 
max(case when ReverseLeveL = 3 then UnitOfMeasure end) as UnitOfMeasure3 
from (
    SELECT *, row_number() over (partition by StartID order by Level desc)-1 as ReverseLeveL 
    from RCTE 
    where Level <= 3 
    ) Q 
group by StartID 
having max(case when ReverseLeveL = 1 then ID end) is not null; 
0

你有表的設計 - 父ID列 - 被稱爲鄰接表。它是分層數據。鄰接表是several ways之一,用於表示SQL中的分層數據。

對於你的問題......

是否有性能的任何其他更好的解決辦法?

是您的層次結構只有三個層次深?大!然後一個3X自我加入就會好起來。這是最直接,最好的方法。使用此方法從SQL執行計劃棒添加任何missing indices


您的層次結構是否有任意深度?爲此,需要遞歸。你現在有幾個選擇。遞歸CTE是最簡單的方法,我建議你使用它,除非性能太差。如果是,那麼簡單的通話做了偉大的工作,here比較可用的各種方法的性能:熱膨脹係數Recusive,動態SQL,while循環,等等

enter image description here

這是一個有點深潛水,但他們最終確定了一個異國情調的表現冠軍。

當遍歷層級結構,我們八 13級深,T 之間構建他基於集合的WHILE循環,避免萬聖節 保護,重新編譯選項(LAHPwR)是最常經過的 冠王。也許令人驚訝的是,非傳統的遞歸函數似乎比六個 和12個級別之間的rCTE略好一點。它與7個 級別的基於集合的循環完全相關。

所以是的,有一些複雜的方式來查詢任意深度鄰接列表。其中一些比其他人表現更好。

更簡單的方法可能只是以不同的分層格式保存數據的副本,而不是異國情調的查詢方法。一個可以更快地查詢。已經顯示嵌套集要被查詢爲orders of magnitudes faster而不是鄰接列表。在this article的底部有一個SP,它顯示瞭如何將鄰接列表轉換爲嵌套集。

祝你好運!

相關問題