从多对多关系中选择完全外部联接

时间:2013-11-19 22:35:32

标签: sql sql-server-2008

我正在尝试在MSSQL中做一些事情,我认为在任何具有多对多关系的数据库中这是一个相当简单和常见的事情。但是我似乎总是得到一个非常复杂的选择查询,我似乎多次重复相同的条件以获得所需的输出。

场景是这样的。我有2个表(表A和B)和一个带有外键到A和B的ID列的交叉表。在crosstable中只能有一对唯一的As和Bs(我想2个外键组成一个交叉表中的主键?!?)。三个表中的数据可能如下所示:

TABLE A          TABLE B            TABLE AB
ID     Type      ID     Type        AID    BID
--------------------------------------------------
R      Up         1      IN         R      3
S      DOWN       2      IN         T      3
T      UP         3      OUT        T      5
X      UP         4      OUT        Z      6
Y      DOWN       5      IN
Z      UP         6      OUT

现在假设我选择UP类型的A中的所有行和OUT类型B中的所有行:

SELECT ID FROM A AS A1
WHERE Type = 'UP'
(Result: R, T, X, Z)

SELECT ID FROM B AS B1
WHERE Type = 'OUT'
(Result: 3, 4, 6)

我现在想要的是根据AB中列出的关系完全外连接这两个子查询。因此,我希望A1和B1中的所有ID至少列出一次:

A.ID    B.ID
R       3
T       3
null    4
X       null
Z       6

从这个结果集中我希望能够看到: - A1中的哪些行与B1中的任何行无关 - B1中的哪些行与A1中的任何行无关 - A1和B1中行之间的关系

我尝试了几件事,例如:

SELECT A1.ID, B1.ID
FROM (
    SELECT * FROM A
    WHERE Type = 'UP') AS A1

FULL OUTER JOIN AB ON
A1.ID = AB.AID

FULL OUTER JOIN (
    SELECT * FROM B
    WHERE Type = 'OUT') AS B1
ON AB.BID = B1.ID 

这不起作用,因为AB中列出的某些关系是A1之间的行和B1中行之间的行NOT IN B1 OR但不是IN A1。

换句话说 - 我似乎也被迫为AB表创建一个子查询:

SELECT A1.ID, B1.ID
FROM (
    SELECT * FROM A
    WHERE Type = 'UP') AS A1

FULL OUTER JOIN (
    SELECT * FROM AB AS AB1
    WHERE
      AID IN (SELECT ID FROM A WHERE type = 'UP') AND
      BID IN (SELECT ID FROM B WHERE type = 'OUT')
) AS AB1 ON
A1.ID = AB1.AID

FULL OUTER JOIN (
    SELECT * FROM B
    WHERE Type = 'OUT') AS B1
ON AB1.BID = B1.ID

对于看似简单的问题,这似乎是一个相当复杂的解决方案。特别是当您考虑具有更多(复杂)条件的A1和B1子查询时 - 可能涉及到其他表的连接(一对多)将需要在AB1子查询中重复相同的临时连接和条件。

我认为必须有一种明显的方法来重写上面的select语句,以避免多次重复相同的条件。解决方案可能就在我面前,但我看不到它。

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:1)

我认为你可以在这种情况下使用CTE,如下所示:

;WITH cte AS (
  SELECT A.ID AS AID, A.Type AS AType, B.ID AS BID, B.Type AS BType
  FROM A FULL OUTER JOIN AB ON A.ID = AB.AID
  FULL OUTER JOIN B ON B.ID = AB.BID)
SELECT AID, BID FROM CTE WHERE AType = 'UP' OR BType = 'OUT'

使用CTE的优点是它将被编译一次。然后,您可以向CTE外的WHERE子句添加其他条件

选中此SQL Fiddle