2017-04-08 4 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の階層データを表します。あなたの質問のために

...

は、パフォーマンスのために、他のよりよい解決策はありますか?

階層のレベルは3段階だけですか?すばらしいです!その後、3X SELF JOINは問題ありません。これは、最も直接的で最も効果的なアプローチです。このアプローチでは、SQL実行プランの中からmissing indicesを追加してください。


階層の深さは任意ですか?そのためには、再帰が必要です。いくつかの選択肢があります。再帰CTEは最も簡単なアプローチです。パフォーマンスが低すぎない限り、これを使用することをお勧めします。それがある場合には、単純なトークが利用できる様々な方法の性能を比較する素晴らしい仕事をしてhere:Recusive CTEは、動的SQLなどループ、一方で...

enter image description here

それは深いのビットです彼らは最終的にエキゾチックなパフォーマンスのチャンピオンに落ち着きます。

トンは彼がセットに基づく、我々は深く8と 13レベル間で構築階層を横断するときRECOMPILEオプション(LAHPwR)でハロウィン 保護を回避しながらループが最も多く、経過 回受賞しています。おそらく驚くべきことに、非伝統的な再帰的な 関数は、 と12のレベルの間のrCTEよりもちょうど良いようです。それは正確に7つの レベルでセットベースのループと結びついています。

はい、任意の深さの隣接リストを照会する複雑な方法があります。それらのうちのいくつかは他のものよりも優れています。

エキゾチックなクエリメソッドの代わりに、より単純なアプローチは、データのコピーを異なる階層形式で保持することです。はるかに速く照会できるもの。隣接リストより照会するネストされたセットはorders of magnitudes fasterであることが示されています。隣接リストをネストされたセットに変換する方法を示すthis articleの下部にSPがあります。

幸運を祈る!

関連する問題