小自我加入查询慢

时间:2017-11-20 00:19:44

标签: sql-server performance join

你有一张可容纳42,400行的表。这对于SQL标准来说非常小。

该表保存设施的位置,并设计为父/子关系。

CREATE TABLE [dbo].[Location](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentLocationId] [int] NULL,
    [Description] [varchar](100) NOT NULL,
    [LocationTypeId] [int] NOT NULL,
    [IsDeleted] [bit] NOT NULL,
 CONSTRAINT [pk_Location] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

它有一个LocationType表的外键,它有5行。

ALTER TABLE [dbo].[Location]  WITH CHECK ADD  CONSTRAINT [fk_location_locationtype] FOREIGN KEY([LocationTypeId])
REFERENCES [ref].[LocationType] ([Id])
GO

它还有一个自我加入。

ALTER TABLE [dbo].[Location]  WITH CHECK ADD  CONSTRAINT [fk_LocationLocation] FOREIGN KEY([ParentLocationId])
REFERENCES [dbo].[Location] ([Id])
GO

父母可以有很多孩子。但是,我们有固定数量的位置级别。并且查询了位置信息。

所以,我已经实现了一个视图来获取位置数据。

它有点长,所以它位于底部,但是,想法是填充一排耳朵水平,然后加入它的孩子,做一个联合并返回结果集。

例如:

选择位置级别= 1(根)的位置 然后 选择位置级别为2的位置,并加入其父级。 然后 选择位置级别为3的位置并加入其父母及其父母。

现在这似乎是一个性能问题。有没有更好的方法来实现我正在寻找的结果,即每个位置一行...

第一行是根节点...... 第二个是根,是它的第一个孩子 第三个是它的第二个孩子的根源。

在特定位置进行选择的示例:

enter image description here

SELECT
    l1.Id as LocationId,
    l1.LocationTypeId, 
    lt.DisplayName,
    l1.ParentLocationId,
    l1.Description AS thisLocationName,
    l1.IsDeleted,
    l1.Description AS Level1, 
    NULL AS Level2, 
    NULL AS Level3,
    NULL AS Level4,
    NULL AS Level5,
    NULL AS Level6,
    NULL AS Level7,
    NULL AS Level8,
    NULL AS Level9,
    NULL AS Level10
FROM [dbo].Location l1
INNER JOIN ref.LocationType lt
   ON lt.Id = l1.LocationTypeId
AND lt.level = 1

UNION 

SELECT 
    l2.Id AS LocationId,
    l2.LocationTypeId,
    lt.DisplayName,
    l2.ParentLocationId,
    l2.Description AS thisLocationName,
    l1.IsDeleted | l2.IsDeleted, 
    l1.Description AS Level1, 
    l2.Description AS Level2, 
    NULL AS Level3,
    NULL AS Level4,
    NULL AS Level5,
    NULL AS Level6,
    NULL AS Level7,
    NULL AS Level8,
    NULL AS Level9,
    NULL AS Level10
FROM [dbo].Location l1
INNER JOIN [dbo].Location l2
    ON l2.ParentLocationId = l1.Id
INNER JOIN ref.LocationType lt
   ON lt.Id = l2.LocationTypeId
AND lt.level = 2

UNION

SELECT 
    l3.Id AS LocationId,
    l3.LocationTypeId,
    lt.DisplayName,
    l3.ParentLocationId,
    l3.Description AS thisLocationName,
    l1.IsDeleted | l2.IsDeleted | l2.IsDeleted,
    l1.Description AS Level1, 
    l2.Description AS Level2, 
    l3.Description AS Level3,
    NULL AS Level4,
    NULL AS Level5,
    NULL AS Level6,
    NULL AS Level7,
    NULL AS Level8,
    NULL AS Level9,
    NULL AS Level10
FROM [dbo].Location l1
INNER JOIN [dbo].Location l2
    ON l2.ParentLocationId = l1.Id
INNER JOIN [dbo].Location l3
    ON l3.ParentLocationId = l2.Id
INNER JOIN ref.LocationType lt
   ON lt.Id = l3.LocationTypeId
AND lt.level = 3

UNION
.... (This occurs for 10 levels)

这里有表格的演示数据。

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(1, NULL, 'A Building', 1)


INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(2, 1, '1st Floor', 2)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(3, 1, '2nd Floor', 2)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(4, 1, '3rd Floor', 2)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(5, 1, '4th Floor', 2)



INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(6, 1, 'Boardroom', 3)


INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(7, 1, 'Main Office', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(8, 1, 'Directors Office', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(9, 1, 'Kitchen', 3)



INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(10, 2, 'Office', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(11, 2, 'Meeting Room', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(12, 2, 'Kitchen', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(13, 2, 'Gents WC', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(14, 2, 'Ladies WC', 3)



INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(15, 3, 'Office 1', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(16, 3, 'Office 2', 3)
INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(17, 3, 'Office 3', 3)
INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(18, 3, 'Office 4', 3)
INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(19, 3, 'Meeting Room', 3)
INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(20, 3, 'Staff Room', 3)



INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(21, 4, 'Small Office', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(22, 4, 'Medium', 3)

INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(23, 4, 'Large Office', 3)
INSERT INTO Location 
(Id, ParentLocationId, Description, LocationTypeId)
VALUES
(24, 4, 'Meeting Room', 3)

我尝试过以下推荐的CTE版本:

CREATE VIEW [dbo].[vwLocations3]
WITH SchemaBinding

AS
with h as (
    select l.Id, l.ParentLocationId, l.Description, l.LocationTypeId, l.IsDeleted
             , convert(varchar(100), null) level2
             , convert(varchar(100), null) level3
             , convert(varchar(100), null) level4
             , convert(varchar(100), null) level5
             , convert(varchar(100), null) level6
             , convert(varchar(100), null) level7
             , convert(varchar(100), null) level8
             , convert(varchar(100), null) level9
    from dbo.Location l
    where ParentLocationId IS NULL
    UNION ALL
    select l.Id, l.ParentLocationId, l.Description, l.LocationTypeId, l.IsDeleted
            , case when l.LocationTypeId = 2 then l.Description when l.LocationTypeId > 2 then h.Description end  level2
            , case when l.LocationTypeId = 3 then l.Description when l.LocationTypeId > 3 then h.Description end  level3
            , case when l.LocationTypeId = 4 then l.Description when l.LocationTypeId > 4 then h.Description end  level4
            , case when l.LocationTypeId = 5 then l.Description when l.LocationTypeId > 5 then h.Description end  level5
            , case when l.LocationTypeId = 6 then l.Description when l.LocationTypeId > 6 then h.Description end  level6
            , case when l.LocationTypeId = 7 then l.Description when l.LocationTypeId > 7 then h.Description end  level7
            , case when l.LocationTypeId = 8 then l.Description when l.LocationTypeId > 8 then h.Description end  level8
            , case when l.LocationTypeId = 9 then l.Description when l.LocationTypeId > 9 then h.Description end  level9
    from h
    inner join dbo.Location l on l.ParentLocationId = h.id
    )
SELECT
        h.Id AS LocationId,
        h.LocationTypeId,
        lt.DisplayName,
        h.ParentLocationId,
        h.Description AS thisLocationName,
        --h.Level1, 
        h.Level2, 
        h.Level3,
        h.Level4,
        h.Level5,
        h.Level6,
        h.Level7,
        h.Level8,
        h.Level9
        --cte.Level10
     FROM h
    INNER JOIN ref.LocationType lt
    ON lt.Id = h.LocationTypeId

但是这将在1,500毫秒内返回一个选择。

1 个答案:

答案 0 :(得分:0)

尝试递归CTE(公用表表达式),例如

WHERE

Demo

with h as (
    select l.Id, l.ParentLocationId, l.Description, l.LocationTypeId, l.IsDeleted
             , convert(varchar(100), null) l2desc
             , convert(varchar(100), null) l3desc
    from Location l
    where ParentLocationId IS NULL
    UNION ALL
    select l.Id, l.ParentLocationId, l.Description, l.LocationTypeId, l.IsDeleted
            , case when l.LocationTypeId = 2 then l.Description when l.LocationTypeId > 2 then h.Description end  l2desc
            , case when l.LocationTypeId = 3 then l.Description when l.LocationTypeId > 3 then h.Description end  l3desc
    from h
    inner join Location l on l.ParentLocationId = h.id
    )
select
*
from h
order by LocationTypeId, ParentLocationId
;