2017-11-20 16 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であり、それの両親への参加を選択。 Then 場所レベル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,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