2016-03-22 7 views
0

私は、刑務所と言えば、場所を保持するテーブルを持っています。階層テーブルの選択

CREATE TABLE [dbo].[Location](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [ParentID] [int] NULL, 
    [LocationTypeID] [int] NOT NULL, 
    [GeoLocation] [geometry] NULL, 
    [Name] [varchar](50) NOT NULL, 
    [Description] [varchar](100) NOT NULL 
CONSTRAINT [pk_location] PRIMARY KEY (ID) 
) 

データファイルここに挿入します。 data.sql file をのParentIDがnullの場合は、それがシステムのルート・ノードです。データは

、我々は刑務所(のTypeID = 5)、建物(のTypeID = 6)、床、細胞及び床を有していてもよいです。親IDは、現在の場所が存在する場所です。

私は、この表に簡単にアクセスできるようにビューを作成しました。

CREATE VIEW [dbo].[vwLocations] 
AS 

WITH MyLocation 
AS (
     SELECT ParentID, 
       ID, 
       Name, 
       0 AS LevelNo, 
       CAST(CAST(LocationTypeID AS VARCHAR) + '|' + Name AS VARCHAR(512)) AS SORT_PATH 
     FROM dbo.Location AS A 
     WHERE  (ParentID IS NULL) 
     UNION ALL 
     SELECT C.ParentID, 
       C.ID, 
       C.Name, 
       MyLocation_1.LevelNo + 1 AS Expr1, 
       CAST(MyLocation_1.SORT_PATH + '//' + CAST(C.LocationTypeID AS VARCHAR) + '|' + C.Name AS VARCHAR(512)) AS SORT_PATH 
     FROM dbo.Location AS C 
     INNER JOIN MyLocation AS MyLocation_1 
      ON MyLocation_1.ID = C.ParentID 
    ) 
    SELECT ISNULL(ID, -999) AS LocationID, 
      ParentID, 
      LevelNo AS LevelNumber, 
      SORT_PATH AS FullPath, 
      Name AS Description 
    FROM MyLocation AS ML 
GO 

これはうまく機能し、すべてのケースで効率的です。開発者は、各コンポーネントに分割したい場合、文字列を取得します。例えば、私は、文字列を返す:|刑務所// 6 |

5をP 1 // 7 | P 1 // 8 | // 9 1 | 01 // 10 | 01

彼らその後、

5を取得するための '//' でスプリット|刑務所 6は、| P 1 7 | P 1 8 | 1 9 | 01 10 | 01

意味し、ID 5は、そのことを意味刑務所、ID 6は建物、ID 7はセクション、ID 8は床、9はセル、10はベッドです。

これらはコード内で行います。

問題は、それらが、例えば、フロアでソートできるようにしたいです。それはできません。何とかしなければならないのは、「Prison」、「Building ...」などと呼ばれる行ごとに余分な列を追加し、それらを移入することです。場所はフロアであれば、例えば、その後、刑務所、ビル、セクションおよびフロア列がデータを持っていますが、残りはnullになりますので、nullの場合も

ので、いくつか。

これは可能ですか?

+0

フィールドを別のフィールドに部分文字列で入力し、ビューとして保存してから、ビュー内の新しいフロア列をソートしましたか? – JLB

+0

@JLB - 私はそれを試みようとしていましたが、それでもビューで行うことはできますか?それは 'CASE'声明でしょうか?しかし、たとえそれがあったとしても、私はCASE文をどのように使用して、異なる列に値を設定できるかはわかりません。または、CASEが最終的な選択であり、5列のCASESステートメントが各列に1つずつありますか?または、2つのビュー、これがプライマリ・データ抽出であること、そして2番目のビューで何らかのソートが行われているということですか? – Craig

+0

リクエストを正しく読んでいる場合は、階層の幅優先検索(BFS)を要求しています。もしそうなら、いくつかのサンプルデータを(私はSSMSにコピーして貼り付けることができ、そこからテーブルを得ることができるような形式で提供することが望ましい)、私はあなたにBFS命令の結果セットを与えるコードを提供することができます。 –

答えて

0

このような任意の解決策の最大の問題(とあなたがしようとする最初じゃないが)データベースエンジンに特別な機能なしで、あなたは任意の深さの木を表現することはできません...レベル1に何かがあります0にノードを持つ親にジョインする必要があります。レベル2にあるものは、レベル1などで親にジョインする必要があります。したがって、それはフロア上でソートしようとする以上のものです正直言って、単にビュー選択の最後にORDER BYでなければなりません)、より基本的には、可能なレベルごとに結合がない限りツリーをトラバースできません。

「木は、リレーショナルデータベースに格納することはできません」と私はhierarchyid型データ型の大ファンだ

1

これを簡素化します。私は追いかけようとしている:

alter table dbo.Location add 
    [Path] hierarchyid null, 
    [Level] as [Path].GetLevel() persisted; 

WITH MyLocation AS (
    SELECT ParentID, 
      ID, 
      Name, 

      cast(concat('/', ID, '/') as varchar(1000)) as [Path] 
    FROM dbo.Location AS A 
    WHERE  (ParentID IS NULL) 
    UNION ALL 
    SELECT C.ParentID, 
      C.ID, 
      C.Name, 
      cast(concat(p.[Path], c.ID, '/') as varchar(1000)) as [Path] 
    FROM dbo.Location AS C 
    INNER JOIN MyLocation AS p 
     ON p.ID = C.ParentID 
) 
update l 
set [Path] = ml.[Path] 
from dbo.Location as l 
join MyLocation as ml 
    on l.ID = ml.ID 
GO 

ここでやったことは、テーブルにHierarchyIDの列を追加することです。これにより私は究極の祖先(あなたの場合は刑務所)まで経路を保存し、経路に沿って先祖を効率的に照会することができます。

次に、テーブル値関数。

create function dbo.tvf_PivotHierarchy(@id int) 
returns table 
as return 

    select ID, 
     [1], 
     [2], 
     [3], 
     [4], 
     [5], 
     [6] 
    from (
     select p.ID, l.[Description], l.[Level] 
     from dbo.Location as l 
     join dbo.Location as p 
      on p.[Path].IsDescendantOf(l.[Path]) = 1 
     where p.ID = @id 
    ) as p 
    pivot (
     max(Description) 
     for [Level] in (
      [1], 
      [2], 
      [3], 
      [4], 
      [5], 
      [6] 
     ) 
    ) as pvt; 
go 

使用例:

with data as (
    select * from (values 
     (67), 
     (115) 
    ) as x(ID) 
) 
select * 
from data as d 
join dbo.Location as l 
    on d.ID = l.ID 
cross apply dbo.tvf_PivotHierarchy(l.ID); 

すべてのことがここで起こって与えられたIDのために、私はそのレコードの祖先であるすべてのレコードを見つけることだし、簡単なピボットを通してそれを実行している、ということです。

ここで、特定の方法でソートする場合は、オプションがあります。深度優先で並べ替える場合(つまり、刑務所、建物、その建物のすべての階、次の建物など)は、最後のクエリにorder by [Path]を投げます。あなたが幅優先(例えば、刑務所、など)をしたい場合は、order by [Level], [Path]を実行してください。

select *のようなやり方でピボットの列に名前を付けないと、結果セットが何が起こっているのか、なぜなら私が怠け者だから説明しているからです。あなたがそれを使用することを選択した場合は、実用のためにこれをきれいにする必要があります。

+0

うわー@ベン!これは非常に興味深いようです。私はそれに行くつもりです。そこには数多くの新しい概念があります。ありがとう。私は戻って報告します。 – Craig

+0

これは本当にうまくいくように見えます!行を挿入して更新を実行しない方法があるかどうかはよく分かりません。現在位置情報を入力するスクリプトは、ソースシステムからの移行によって実行されます。行を挿入するときにパスとレベルを設定する方法はありますか? – Craig

+0

はい。 2つのシナリオがあります.1つは挿入前のIDとそうでないIDです。前者では、ID値を取得するために挿入を行う必要があります。後者の場合は、一度にすべてを行うことができます。いずれにしても、親のパスを笑い、子供のIDとそれにスラッシュを付けることでパスを得ることができます。レベルは計算カラムなので、それを心配する必要はありません。 –

関連する問題