2017-11-20 3 views
0

안녕 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] 

은 5 개 행을 가지고 locationType에 테이블에 외부 키가 있습니다.

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을 선택하고 부모와 그 부모와 합류합니다.

현재 성능 문제가있는 것 같습니다. 먼저 아이의와 나는 위치에 따라 행 인을 찾고 결과를 달성하기 위해 더 나은 방법 ...

첫 번째 행은 루트 노드입니다 ... 2 루트가 존재입니다

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) 

enter image description here

여기 테이블에 대한 그래서 데모 데이터입니다 :

.. 그것으로 뿌리가 두번째 아이의 특정 위치에 선택을하는 예이다.

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,500ms의 선택을 반환합니다.

+0

UNION은 UNION ALL –

+0

감사 @Used_By_Already보다 느린, 중복을 제거합니다. – Craig

+1

질문에 샘플 데이터를 추가하십시오 (모든 10 레벨은 필요하지 않고 몇 줄만 사용). 이미지를 사용하지 마십시오. 또한 해당 데이터 샘플에서 예상되는 결과를 추가하십시오. –

답변

0

재귀 CTE (공통 표 표현식)를 사용해보십시오. 내보기를 개정,하지만이 차이를하지 -

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 
; 

Demo

| | Id | ParentLocationId | Description | LocationTypeId | IsDeleted | l2desc |  l3desc  | 
|----|----|------------------|------------------|----------------|-----------|------------|------------------| 
| 1 | 1 | NULL    | A Building  |    1 | False  | NULL  | NULL    | 
| 2 | 2 | 1    | 1st Floor  |    2 | False  | 1st Floor | NULL    | 
| 3 | 3 | 1    | 2nd Floor  |    2 | False  | 2nd Floor | NULL    | 
| 4 | 4 | 1    | 3rd Floor  |    2 | False  | 3rd Floor | NULL    | 
| 5 | 5 | 1    | 4th Floor  |    2 | False  | 4th Floor | NULL    | 
| 6 | 6 | 1    | Boardroom  |    3 | False  | A Building | Boardroom  | 
| 7 | 7 | 1    | Main Office  |    3 | False  | A Building | Main Office  | 
| 8 | 8 | 1    | Directors Office |    3 | False  | A Building | Directors Office | 
| 9 | 9 | 1    | Kitchen   |    3 | False  | A Building | Kitchen   | 
| 10 | 10 | 2    | Office   |    3 | False  | 1st Floor | Office   | 
| 11 | 11 | 2    | Meeting Room  |    3 | False  | 1st Floor | Meeting Room  | 
| 12 | 12 | 2    | Kitchen   |    3 | False  | 1st Floor | Kitchen   | 
| 13 | 13 | 2    | Gents WC   |    3 | False  | 1st Floor | Gents WC   | 
| 14 | 14 | 2    | Ladies WC  |    3 | False  | 1st Floor | Ladies WC  | 
| 15 | 15 | 3    | Office 1   |    3 | False  | 2nd Floor | Office 1   | 
| 16 | 16 | 3    | Office 2   |    3 | False  | 2nd Floor | Office 2   | 
| 17 | 17 | 3    | Office 3   |    3 | False  | 2nd Floor | Office 3   | 
| 18 | 18 | 3    | Office 4   |    3 | False  | 2nd Floor | Office 4   | 
| 19 | 19 | 3    | Meeting Room  |    3 | False  | 2nd Floor | Meeting Room  | 
| 20 | 20 | 3    | Staff Room  |    3 | False  | 2nd Floor | Staff Room  | 
| 21 | 21 | 4    | Small Office  |    3 | False  | 3rd Floor | Small Office  | 
| 22 | 22 | 4    | Medium   |    3 | False  | 3rd Floor | Medium   | 
| 23 | 23 | 4    | Large Office  |    3 | False  | 3rd Floor | Large Office  | 
| 24 | 24 | 4    | Meeting Room  |    3 | False  | 3rd Floor | Meeting Room  | 
+0

감사합니다. 나는 그것을 실행했지만 설명, l2desc 및 l3desc는 모두 동일합니다. 설명은 현재 위치에 대한 설명이어야하며 l1은이 위치가 속한 트리의 루트 여야하고 l2는 그 루트 ... 선택된 위치로 재귀 적으로. CTE가 작동하는 방식을 연구하려고합니다. 좋은 단계 인 것 같지만 결과가 잘못되었습니다. – Craig

+0

죄송합니다. 예제를 수정했지만 모든 설명 열을 완전히 해결하지 못했을 수도 있습니다. –

+0

이것을 시도했지만 44,000 개의 레코드로 인해 실제로 나쁜 결과가 나타납니다. 내 원래 쿼리는 1,100ms에 반환됩니다, 내가 조회 테이블에 가입을 제거하고 새로운 300ms에 다시 만들어 ... cte 버전 1,500ms입니다 ... – Craig