2017-12-09 13 views
0

私は、親子関係の可能性のある以下のような単純なテーブルを持っています。これは実際には非常に大きなテーブルですが、これは公正な表現です。 「孫」の関係はありません。シンプルだが非効率な親子関係クエリを改善する方法

私はこれをある入力値でフィルタリングされた少し異なるテーブルに変換する必要があります。

declare @pc table (myId char(1) not null, parentId char(1) ); 

    insert into @pc (myId, parentId) values ('A', null) 
    insert into @pc (myId, parentId) values ('B', 'A') 
    insert into @pc (myId, parentId) values ('C', 'A') 
    insert into @pc (myId, parentId) values ('D', null) 
    insert into @pc (myId, parentId) values ('E', null) 
    insert into @pc (myId, parentId) values ('F', 'E') 
    insert into @pc (myId, parentId) values ('G', null) 
    insert into @pc (myId, parentId) values ('H', 'G') 
    insert into @pc (myId, parentId) values ('I', 'G') 
    insert into @pc (myId, parentId) values ('J', 'G') 
    insert into @pc (myId, parentId) values ('K', null) 

    -- This is the results I need 
    declare @target table (myId char(1) not null, parentId char(1), hasFamily bit); 
"A" の

与えられた入力は、その後、私はこのような3行必要があります:A.私は "家族・グループ" に属しているすべてのものを必要とする、つまり

A NULL 1 
    B A 1 
    C A 1 

「D」を考える
A NULL 1 
    B A 1 
    C A 1 

私はちょうど必要があります:「B」私は同じ出力、(Aさんであることを起こる)Bの家族グループ内のすべてを必要と考えます誰がD'sの家族の中ではありませんので、1行が:

D NULL 0 

nullを考えると、私はセットテーブル全体のデータを必要とするが、適切な行を「持つ家族」かではないとしてマーク。ここで

は、技術的に正しいです私の試みだが、それはそれを行うには、データの3回のパスを取るすべてでは効率的ではありません。

declare @testcase char(1) = 'B'; 

    -- The inefficient method 
    INSERT INTO @target(myId,parentId) 
      SELECT pc.myId, pc.parentId 
      FROM @pc pc 
      WHERE(pc.myId = ISNULL(@testcase, pc.myId)) 
       OR (pc.parentId [email protected]); 

    INSERT INTO @target(myId,parentId) 
      SELECT pc.myId, pc.parentId 
      FROM @pc pc 
      WHERE pc.myId IN (SELECT parentId FROM @target) 
       AND pc.myId NOT IN (SELECT myId FROM @target); 

    update t 
     set t.hasFamily = 1 
     from @target t 
     left outer join @target t2 
     on t.myId = t2.parentId 
     where t.parentId is not null or t2.myId is not null; 

あなたはこの問題を見ているのより良い方法を見ることができますか?

答えて

1

子供をカウントするウィンドウ関数は、すべての行で使用できるようになり、必要なクエリを簡略化します。あなたがそれを配備する方法を選んだのはあなたです。たとえば、永続化された計算カラムを使用するか、トリガーを使用するかを選択できます。規模とパフォーマンスが問題であれば、インデックス作成を検討し、実行計画を検査する必要があります。

おそらく私はあなたがここに提示した小さなモデルから複雑さを見ないので、私の提案は単純すぎるかもしれません。

declare @want char(1) = 'A' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| A | (null) |   1 | 
| B |  A |   1 | 
| C |  A |   1 | 

クエリ2 SQL Fiddle

create table Table1 (myId char(1) not null, parentId char(1) ); 

insert into Table1 (myId, parentId) values ('A', null); 
insert into Table1 (myId, parentId) values ('B', 'A'); 
insert into Table1 (myId, parentId) values ('C', 'A'); 
insert into Table1 (myId, parentId) values ('D', null); 
insert into Table1 (myId, parentId) values ('E', null); 
insert into Table1 (myId, parentId) values ('F', 'E'); 
insert into Table1 (myId, parentId) values ('G', null); 
insert into Table1 (myId, parentId) values ('H', 'G'); 
insert into Table1 (myId, parentId) values ('I', 'G'); 
insert into Table1 (myId, parentId) values ('J', 'G'); 
insert into Table1 (myId, parentId) values ('K', null); 

create view too_simplistic as 
select 
     myId 
    , parentId 
    , coalesce(parentId, myId) parent_flat 
    , case when count(*) over(partition by coalesce(parentId, myId))-1 = 0 
       then 0 
       else 1 
     end as hasFamily 
    from table1 
; 

クエリ1

デモ

declare @want char(1) = 'B' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results:3

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| A | (null) |   1 | 
| B |  A |   1 | 
| C |  A |   1 | 

問合せ:

declare @want char(1) = 'D' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| D | (null) |   0 | 

クエリ4

select * 
from too_simplistic 

Results

| myId | parentId | parent_flat | hasFamily | 
|------|----------|-------------|-----------| 
| A | (null) |   A |   1 | 
| B |  A |   A |   1 | 
| C |  A |   A |   1 | 
| D | (null) |   D |   0 | 
| E | (null) |   E |   1 | 
| F |  E |   E |   1 | 
| G | (null) |   G |   1 | 
| H |  G |   G |   1 | 
| I |  G |   G |   1 | 
| J |  G |   G |   1 | 
| K | (null) |   K |   0 | 



select d.* 
    , case when children = 0 then 0 else 1 end as hasFamily 
from (
    select * 
     , count(*) over(partition by coalesce(parentId, myId))-1 children 
    from @pc 
    ) d 
; 
+0

はい、これはすべての行にhasFamilyフラグを設定する役立ちますが、それはまた別のネストされた2つのサブクエリを取りますか、および/または(不正なクエリプランの原因と思われる)一連のグループを検索します。 –

+0

これは推測か、実際の勉強計画はありますか?データのパスは1回だけで、入れ子のレベルは1つのみです。ウィンドウ関数は非常に効率的です。 –

+0

"と" nor "または"私の提案されたクエリで使用されています。だから私はなぜあなたがそこにあると言っているのか理解できない。 –