2014-10-20 168 views
0

我有一個代表我產品結構的樹狀分層結構。

在每個產品級別(6個級別)上,我都有一個銷售價格鏈接到它。我使用兩張相互連接的表格將較低層次的價格與上面的價格聯繫起來。

我想這樣做,以便我不會多次考慮價格。這是用下面的代碼(我只用0級,1和2顯示的想法通知)來完成:Hiearchical Join需要很長時間

SELECT L0_SALESPRICE 
     ,L1_SALESPRICE 
     ,L2_SALESPRICE 

FROM 
(SELECT DISTINCT A.* 
FROM BCT A 
JOIN QuotationLine QL ON A.PRICECALCID = QL.PRICECALCID 
WHERE A.Levels = 0) AS L0 

JOIN 
(SELECT DISTINCT A.* 
FROM BCT A 
JOIN QuotationLine QL ON A.PRICECALCID = QL.PRICECALCID 
WHERE A.Levels = 1) AS L1 ON L0.ItemId = L1.ParentItemId 

JOIN 
(SELECT DISTINCT A.* 
FROM BCT A 
JOIN QuotationLine QL ON A.PRICECALCID = QL.PRICECALCID 
WHERE A.Levels = 2) AS L2 ON L1.ItemId = L2.ParentItemId 

的問題是,查詢永遠不會完成執行,並且我得到一個內存不足的錯誤。

表格BCT是750 000行,表格QuotationLine是22000行。

任何意見表示讚賞。

+0

由於行數的原因,這需要很長時間。你真的需要把他們全部拿走嗎? – DavidG 2014-10-20 08:23:10

+0

首先推出查詢計劃,以便我們瞭解您是否錯過索引等。然後描述您的硬件 - 像這樣的東西在適當的中檔服務器上並不那麼困難,但對於小的東西 - 哎喲。 – TomTom 2014-10-20 08:36:19

+0

嘗試使用'CASE'表達式'WHEN A.Levels = 0',這樣你只需要查詢表格一次。你能提供一些DDL或SQLFiddle嗎? – NickyvV 2014-10-20 08:46:01

回答

0

爲演示如何解決此問題,以下是一些示例表定義。

CREATE TABLE [Description] 
(
    [DescriptionId] INT IDENTITY NOT NULL CONSTRAINT [PK_Description] PRIMARY KEY, 
    [DescriptionText] NVARCHAR(50) NOT NULL 
) 
GO 

CREATE TABLE [Hierarchy] 
(
    [HierarchyId] INT NOT NULL CONSTRAINT [PK_Hierarchy] PRIMARY KEY, 
    [ParentHierarchyId] INT NULL CONSTRAINT [FK_Hierarchy_ParentHierarchyId] REFERENCES [Hierarchy] ([HierarchyId]) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    [Price] MONEY NOT NULL, 
    [DescriptionId] INT NULL CONSTRAINT [FK_Hierarchy_Description] REFERENCES [Description] ([DescriptionId]) ON DELETE SET NULL ON UPDATE CASCADE 
) 
GO 

CREATE INDEX [IX_Hierarchy_ParentHierarchyId] ON [Hierarchy] 
([ParentHierarchyId]) INCLUDE ([HierarchyId], [Price], [DescriptionId]) 
GO 

一個天真的方式來獲得一個層次 - 也就是說,一個是不可能解決您的性能問題 - 可能是:

;WITH RowEnds AS 
(
    SELECT h.[HierarchyId], h.[ParentHierarchyId], h.[Price], h.[DescriptionId], h.[HierarchyId] AS [RowEndHierarchyId], 0 AS [ReverseLevel] 
    FROM [Hierarchy] h 
    WHERE NOT EXISTS (SELECT 1 FROM [Hierarchy] i WHERE i.[ParentHierarchyId] = h.[HierarchyId]) 
    UNION ALL 
    SELECT h.[HierarchyId], h.[ParentHierarchyId], h.[Price], h.[DescriptionId], r.[RowEndHierarchyId], r.[ReverseLevel] + 1 AS [ReverseLevel] 
    FROM [Hierarchy] h 
    INNER JOIN RowEnds r ON h.[HierarchyId] = r.[ParentHierarchyId] 
), 
InOrder AS 
(
    SELECT r.RowEndHierarchyId, r.[HierarchyId], r.Price, d.DescriptionText, RANK() OVER (PARTITION BY r.[RowEndHierarchyId] ORDER BY r.[ReverseLevel] DESC) AS [Level] 
    FROM RowEnds r 
    LEFT JOIN [Description] d ON r.DescriptionId = d.DescriptionId 
) 
SELECT DISTINCT o.RowEndHierarchyId, p.[1] AS Price1, d.[1] AS Description1, p.[2] AS Price2, d.[2] AS Description2, p.[3] AS Price3, d.[3] AS Description3, 
     p.[4] AS Price4, d.[4] AS Description4, p.[5] AS Price5, d.[5] AS Description5, p.[6] AS Price6, d.[6] AS Description6, 
     p.[7] AS Price7, d.[7] AS Description7 
FROM InOrder o 
INNER JOIN 
(SELECT projp.RowEndHierarchyId, projp.[Level], projp.[Price] 
FROM InOrder projp) ppre 
PIVOT (MIN([Price]) FOR [Level] IN ([1], [2], [3], [4], [5], [6], [7])) p 
ON o.RowEndHierarchyId = p.RowEndHierarchyId 
LEFT JOIN 
(SELECT projd.RowEndHierarchyId, projd.[Level], projd.DescriptionText 
FROM INOrder projd) dpre 
PIVOT (MIN(DescriptionText) FOR [Level] IN ([1], [2], [3], [4], [5], [6], [7])) d 
ON o.RowEndHierarchyId = d.RowEndHierarchyId 
ORDER BY o.RowEndHierarchyId 

這個例子,當然,使用遞歸公用表表達式來獲得層次結構。該查詢不是從樹的根部開始,而是朝着樹葉工作,而是採用相反的方法。這樣做的好處是輸出中的每一行都對應樹中的一個葉節點。

但是,這種方法的性能可能仍然不理想,因爲您沒有機會對公共表表達式進行索引,其結果集可能非常大。假設tempdb具有足夠的空間和性能,以下更詳細的查詢可能會提高性能。

CREATE TABLE #RowEnd 
(
    [RowEndHierarchyId] INT NOT NULL, 
    [HierarchyId] INT NOT NULL, 
    [ParentHierarchyId] INT NULL, 
    [Price] MONEY NOT NULL, 
    [DescriptionId] INT NULL, 
    [ReverseLevel] INT NOT NULL, 
    PRIMARY KEY ([RowEndHierarchyId], [ReverseLevel] DESC) 
) 

CREATE INDEX [IX_RowEnd_ParentHierarchyId] ON #RowEnd 
([ParentHierarchyId], [RowEndHierarchyId], [ReverseLevel]) 

CREATE INDEX [IX_RowEnd_ReverseLevel] ON #RowEnd 
([ReverseLevel] DESC, [ParentHierarchyId], [RowEndHierarchyId]) 

INSERT #RowEnd ([HierarchyId], [ParentHierarchyId], [Price], [DescriptionId], [RowEndHierarchyId], [ReverseLevel]) 
SELECT h.[HierarchyId], h.[ParentHierarchyId], h.[Price], h.[DescriptionId], h.[HierarchyId], 1 
FROM [Hierarchy] h 
WHERE NOT EXISTS (SELECT 1 FROM [Hierarchy] i WHERE i.ParentHierarchyId = h.[HierarchyId]) 

DECLARE @ReverseLevel INT 
SET @ReverseLevel = 0 

WHILE EXISTS (SELECT 1 FROM #RowEnd re WHERE re.ReverseLevel > @ReverseLevel) 
BEGIN 
    SET @ReverseLevel = @ReverseLevel + 1 
    INSERT #RowEnd ([HierarchyId], [ParentHierarchyId], [Price], [DescriptionId], [RowEndHierarchyId], [ReverseLevel]) 
    SELECT h.[HierarchyId], h.[ParentHierarchyId], h.[Price], h.[DescriptionId], re.[RowEndHierarchyId], @ReverseLevel + 1 
    FROM [Hierarchy] h 
    INNER JOIN #RowEnd re ON re.ParentHierarchyId = h.[HierarchyId] AND re.ReverseLevel = @ReverseLevel 
END 

CREATE TABLE #Price 
(
    RowEndHierarchyId INT NOT NULL PRIMARY KEY, 
    [1] MONEY NULL, 
    [2] MONEY NULL, 
    [3] MONEY NULL, 
    [4] MONEY NULL, 
    [5] MONEY NULL, 
    [6] MONEY NULL, 
    [7] MONEY NULL 
) 

INSERT #Price (RowEndHierarchyId, [1], [2], [3], [4], [5], [6], [7]) 
SELECT p.RowEndHierarchyId, p.[1], p.[2], p.[3], p.[4], p.[5], p.[6], p.[7] 
FROM (SELECT re.RowEndHierarchyId, re.Price, RANK() OVER (PARTITION BY re.RowEndHierarchyId ORDER BY re.ReverseLevel DESC) AS [Level] 
     FROM #RowEnd re) ppre 
     PIVOT (MIN([Price]) FOR [Level] IN ([1], [2], [3], [4], [5], [6], [7])) p 

CREATE TABLE #Description 
(
    RowEndHierarchyId INT NOT NULL PRIMARY KEY, 
    [1] NVARCHAR(50) NULL, 
    [2] NVARCHAR(50) NULL, 
    [3] NVARCHAR(50) NULL, 
    [4] NVARCHAR(50) NULL, 
    [5] NVARCHAR(50) NULL, 
    [6] NVARCHAR(50) NULL, 
    [7] NVARCHAR(50) NULL 
) 

INSERT #Description (RowEndHierarchyId, [1], [2], [3], [4], [5], [6], [7]) 
SELECT d.RowEndHierarchyId, d.[1], d.[2], d.[3], d.[4], d.[5], d.[6], d.[7] 
FROM (SELECT re.RowEndHierarchyId, dt.DescriptionText, RANK() OVER (PARTITION BY re.RowEndHierarchyId ORDER BY re.ReverseLevel DESC) AS [Level] 
     FROM #RowEnd re 
     LEFT JOIN [Description] dt ON re.DescriptionId = dt.DescriptionId) dpre 
     PIVOT (MIN([DescriptionText]) FOR [Level] IN ([1], [2], [3], [4], [5], [6], [7])) d 

SELECT p.RowEndHierarchyId, 
     p.[1] AS Price1, d.[1] AS Description1, 
     p.[2] AS Price2, d.[2] AS Description2, 
     p.[3] AS Price3, d.[3] AS Description3, 
     p.[4] AS Price4, d.[4] AS Description4, 
     p.[5] AS Price5, d.[5] AS Description5, 
     p.[6] AS Price6, d.[6] AS Description6, 
     p.[7] AS Price7, d.[7] AS Description7 
FROM #Price p 
INNER JOIN #Description d ON p.RowEndHierarchyId = d.RowEndHierarchyId 
ORDER BY p.RowEndHierarchyId 

DROP TABLE #Description 
DROP TABLE #Price 
DROP TABLE #RowEnd 

獲取層次結構的基本邏輯與先前的版本類似。但是,以這種方式爲臨時表建立索引可能會大大提高查詢性能。