将表1连接到表2中的第1列或第2列,没有重复

时间:2012-11-14 13:38:20

标签: sql sql-server sql-server-2008

[MS SQL 2008]

我有表(所有列都是字符串名称): 答:两个列将一些数据字段与一个拥有实体相关联 B:定义实体层次结构的三列

我需要创建整个层次结构的单个表(包括两个表中不存在的所有行),但表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

我尝试了两种不同的JOIN的各种UNION,但似乎无法摆脱多余的行......

  1. B LEFT JOIN A ON Acol2=Bcol1 UNION B LEFT JOIN A ON Acol2=Bcol2; 给出重复的行,因为union的第二部分必须将Bcol1设置为NULL (也许一种解决方案是删除这个重复的NULL行的方法?)
  2. B INNER JOIN A ON Acol2=Bcol1 UNION B INNER JOIN A ON Acol2=Bcol2; 显然,从A和B中删除没有共享密钥的所有行 (关于重新获得那些行的简单方法的解决方案?)
  3. 任何想法都赞赏!

    玩: [SQL已删除 - 请参阅回复评论中的小提琴]

1 个答案:

答案 0 :(得分: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)

当您说no duplicates时,仅当ACol2仅出现在Table2中的一行的一个字段中时,才可能这样做。如果它出现在多个地方,您将获得重复    - 如果可能的话,你想如何从Table2中选择哪条记录?


此外,一般情况下,这是一个SQL-Anti-Pattern。

这是因为联接更喜欢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)(B_Val) ...

对该表进行索引

然后在non_normalised表中包含B_ID字段...

  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上有一个索引,在Bcol2上有第二个索引......

  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)

但那太乱了......