父子SQL中查找同一行中最顶层父级的错误性能

时间:2016-02-10 15:19:21

标签: sql-server tsql

我有一个大约有220万行的表,如果行有一行,我希望每行最顶层的父(root)。下面你可以看到我的查询只有一行。

此语句大约需要45秒,这需要一些时间来运行此查询。并非所有人都拥有父密钥,因此大约有100万人没有父母。这可能是值得思考的问题。但希望你们中的一些人能够更好地解决这个问题,我希望你能分享它。

WITH allRows
     AS (SELECT Organisasjonsnummer AS ID,
                Navn,
                Organisasjonsnummer   [RootId],
                Navn [RootName]
         FROM   Virksomhetstjeneste.Virksomhet
         WHERE  Hovedenhet_id IS NULL
         UNION ALL
         SELECT a1.Organisasjonsnummer AS ID,
                a1.Navn,
                a2.[RootId],
                a2.[RootName]
         FROM   Virksomhetstjeneste.Virksomhet a1
                JOIN allRows a2
                  ON a2.ID = a1.Hovedenhet_id)
SELECT *
FROM   allRows 
Where ID = 980659763

结果

ID          Navn                    RootId          RootName
980659763   NILLE AS AVD ALTA   953581477   NILLE AS

1 个答案:

答案 0 :(得分:1)

我多次成为hierarchyid的粉丝。以下是我如何根据您的情况来做这件事。

首先要做的事情:请原谅我没有使用你的表名和列名;我不会说挪威语,在英语之间来回走动,这对我来说很容易出错。所以这就是设置:

USE [tempdb];

IF OBJECT_ID('dbo.myTable') IS NOT NULL
    DROP TABLE [dbo].[myTable];

CREATE TABLE [dbo].[myTable]
    (
      [ID] INT NOT NULL ,
      CONSTRAINT [PK_myTable] PRIMARY KEY ( [ID] ) ,
      [ParentID] INT NULL ,
      [Name] VARCHAR(50) NOT NULL ,
      [Path] HIERARCHYID NULL,
      [Root] AS [Path].GetAncestor([Path].GetLevel() - 1) PERSISTED
    );

INSERT  INTO [dbo].[myTable]
        ( [ID], [ParentID], [Name] )
VALUES  ( 1, NULL, '1' ),
        ( 2, 1, '2' ),
        ( 3, 1, '3' ),
        ( 4, 2, '4' );
WITH    [allRows]
          AS (
               SELECT   [ID] ,
                        [ParentID] ,
                        CAST(CONCAT('/', [ID], '/') AS VARCHAR(MAX)) AS [Path]
               FROM     [dbo].[myTable]
               WHERE    [ParentID] IS NULL
               UNION ALL
               SELECT   [child].[ID] ,
                        [child].[ParentID] ,
                        CAST(CONCAT([parent].[Path], [child].[ID], '/') AS VARCHAR(MAX)) AS [Path]
               FROM     [dbo].[myTable] AS [child]
               JOIN     [allRows] AS [parent]
                        ON [parent].[ID] = [child].[ParentID]
             )
    UPDATE  [m]
    SET     [m].[Path] = [a].[Path]
    FROM    [dbo].[myTable] AS [m]
    JOIN    [allRows] AS [a]
            ON [a].[ID] = [m].[ID];

这只是走私父/子层次结构的标准递归CTE。这里的诀窍是,我正在计算一些我可以用作hierarchyid的东西。遍历层次结构后,我使用计算的层次结构更新基表。

由于您提到您的表格很大,您可能希望批量处理这些更新。我将此作为练习留给读者。另请注意,这是一次性操作(尽管您必须使[Path]列保持最新的插入/更新/删除;我还将此作为练习留给读者。)

现在您已经将行级存储在行中,您可以做魔术:

SELECT  [child].[ID] ,
        [child].[Name] ,
        [root].[ID] ,
        [root].[Name]
FROM    [dbo].[myTable] AS [child]
JOIN    [dbo].[myTable] AS [root]
    ON [root].[Path] = [child].[Root]
WHERE child.[ID] = 4;

这就是说我现在可以通过加入获得给定ID的顶级祖先。将根作为持久计算列是a)花哨和b)不必要的;它只是使最后一个选择更清洁。

如果您不想这样做,可以完全删除[Root]列,然后将联接谓词变为[root].[Path] = [child].[Path].GetAncestor([Path].GetLevel() - 1)

最后,请记住hierarchyid数据类型本机可索引。因此,您可以对[Path][Root]或两者进行索引,这样可能会提高性能。