【问题标题】:Hiearchical Join takes long time分层连接需要很长时间
【发布时间】:2014-10-20 08:09:29
【问题描述】:

我有一个代表我的产品结构的树形层次结构。

在每个产品级别(6 个级别)上,我都有一个与之关联的销售价格。我正在使用两个相互连接的表格将较低级别的价格与较高级别的价格联系起来。

我想这样做,这样我就不会多次考虑价格.这是通过以下代码完成的(请注意,我仅使用级别 0、1 和 2 来展示这个想法):

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

感谢任何建议。

【问题讨论】:

  • 这需要很长时间,因为行数。您真的需要全部获取吗?
  • 首先推出查询计划,以便我们了解您是否错过了索引等。然后描述您的硬件 - 这样的事情在适当的中端服务器上并不难,但在一些小东西上- 哎哟。
  • 尝试使用CASE 表达式和WHEN A.Levels = 0,这样您只需查询表一次。你能提供一些 DDL 或 SQLFiddle 吗?
  • 有使用ql表的理由吗?它的列不在任何地方使用
  • David - 是的,我需要扫描所有行。我也认为这是因为行数,但它应该是可行的......我想。

标签: sql sql-server tree structure


【解决方案1】:

为了演示如何解决此问题,以下是一些示例表定义。

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

获取层次结构的基本逻辑与之前的版本类似。但是,以这种方式索引临时表可能会显着提高查询性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-02-06
    • 1970-01-01
    相关资源
    最近更新 更多