0

[MS SQL 2008]

私はテーブルを持っています (すべての列は文字列名です): A: いくつかのデータフィールドを所有エンティティに関連付ける 2 つの列 B: エンティティの階層を定義する 3 つの列

階層全体 (両方のテーブルに存在しないすべての行を含む) の単一のテーブルを作成する必要がありますが、テーブル A のキー列 (Acol2 として表示) は、テーブル B の列 1 または 2 のいずれかに配置できます...

A:                         B:
 Acol1 | Acol2                Bcol1 | Bcol2 | Bcol3
-------+------              --------+-------+------
   A   |   B                    B   |   X   |   Y
   C   |   D                    Q   |   X   |   Y
   E   |   F                    H   |   D   |   Z
   G   |   H                    W   |   V   |   U

出力は

Hierarchy:
 Acol1 | Bcol1 | Bcol2 | Bcol3
-------+-------+-------+------
   A   |   B   |   X   |  Y
  Null |   Q   |   X   |  Y
   C   |  Null |   D   |  Z
   G   |   H   |   D   |  Z
   E   |  Null |  Null | Null
  Null |   W   |   V   |  U 

Logic (also added to original):
    If A has no record in B, show A with all Null
    If A has record in Bcol1, show A with full row B
    If A has record in Bcol2, show A with Null, Bcol2, Bcol3
    If B has no record in A, show B with Null for Acol1

私は2つの別々のJOINのあらゆる種類のUNIONを試しましたが、不要な行を取り除くことができないようです...

  1. B LEFT JOIN A ON Acol2=Bcol1 UNION B LEFT JOIN A ON Acol2=Bcol2; ユニオンの 2 番目の部分で Bcol1 を NULL に設定する必要があるため、重複する行が生成されます (おそらく、この重複した NULL 行を削除する方法が 1 つありますか?)
  2. B INNER JOIN A ON Acol2=Bcol1 UNION B INNER JOIN A ON Acol2=Bcol2; 明らかに、共有キーを持たない A と B からすべての行を削除します (これらの行だけを取り戻す簡単な方法の解決策は?)

どんなアイデアでも大歓迎です!

再生するには: [SQL を削除 - 返信コメントのフィドルを参照]

4

1 に答える 1

4
SELECT
  Table1.ACol1,
  CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
  Table2.BCol2,
  Table2.BCol3
FROM
  Table1
FULL OUTER JOIN
  Table2
    ON Table1.ACol2 IN (Table2.BCol1, Table2.BCol2)

あなたが言うとき、これはTable2 の 1 つの行の 1 つのフィールドにのみ表示されるno duplicates場合にのみ可能です。ACol2複数の場所に表示される場合は、重複します。
- 可能であれば、Table2 からどのレコードを選択しますか?


また、一般的に、これは SQL アンチパターンです。

これは、結合が Table2 のインデックスを優先するためです。しかし、結合しているフィールドがわからないため、結合条件を満たす単一のインデックスはありません。


編集

これを大幅に高速化するのは、正規化された TableB を作成することです...

 B_ID | B_Col | B_Val
------+-------+-------
   1  |   1   |   B
   1  |   2   |   X
   1  |   3   |   Y
   2  |   1   |   Q
   2  |   2   |   X
   2  |   3   |   Y
   3  |   1   |   H
   3  |   2   |   D
   3  |   3   |   Z
   4  |   1   |   W
   4  |   2   |   V
   4  |   3   |   U

(B_ID)次に、そのテーブルにand on でインデックスを付け(B_Val)ます...

次にB_ID、non_normalized テーブルにフィールドを含めます...

  ID  | Bcol1 | Bcol2 | Bcol3
------+-------+-------+-------
  1   |   B   |   X   |   Y
  2   |   Q   |   X   |   Y
  3   |   H   |   D   |   Z
  4   |   W   |   V   |   U

次に、次のクエリを使用します...

SELECT
  Table1.ACol1,
  CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
  Table2.BCol2,
  Table2.BCol3
FROM
  (
    Table1
  LEFT JOIN
    Table2Normalised
      ON  Table2Normalised.B_Val = Table1.ACol2
      AND Table2Normalised.B_Col IN (1,2)
  )
FULL OUTER JOIN
  Table2
    ON Table2Normalised.B_ID = Table2.ID


編集:

スキーマを変更せずに、代わりに BCol1 に 1 つのインデックスを作成し、Bcol2 に 2 つ目のインデックスを作成します...

  SELECT ACol1, BCol1, BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol1
  UNION ALL
  SELECT ACol1, NULL,  BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol2
  UNION ALL
  SELECT ACol1, NULL,  NULL,  NULL  FROM Table1 a WHERE NOT EXISTS (SELECT * FROM Table2 WHERE BCol1 = a.ACol2)
                                                    AND NOT EXISTS (SELECT * FROM Table2 WHERE BCol2 = a.ACol2)
  UNION ALL
  SELECT NULL,  BCol1, BCol2, BCol3 FROM Table2 b WHERE NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol1)
                                                    AND NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol2)

しかし、それはかなり厄介です...

于 2012-11-14T13:40:55.283 に答える