左外连接与同一个表作为外连接的一部分

时间:2011-12-20 12:53:19

标签: sql left-join outer-join

我想知道如何通过编写外连接查询来获得所需的输出(如下所述) 我在外部加入的表是声明

中其他连接条件的一部分

给出以下数据结构 - 表A是包含一些任意对象的主表 - 表B由A引用,其中A.TYPE_ID = B.ID - 表C定义表A中对象之间的关系,其中C.SOURCE_ID引用A.ID和C.TARGET_ID引用A.ID

这是架构的定义方式,我无法做任何事情(它是遗留系统)

TABLE_A                 
---------------------------
| ID  | TYPE_ID | Name    |
|-------------------------|
| 1   | 1       | Name 1  |
| 2   | 2       | Name 2  |
| 3   | 1       | Name 3  |
| 4   | 1       | Name 4  |
| 5   | 3       | Name 5  |
|-------------------------|

TABLE_B
----------------------
| ID  | TYPE_NAME    |
|--------------------|
| 1   | Type 1       |
| 2   | Type 2       |
| 3   | Type 3       |
| 4   | Type 4       |
|--------------------|

TABLE_C
-------------------------------
| PK  | SOURCE_ID | TARGET_ID |
|-----------------------------|
| 11  | 2         | 1         |
| 12  | 2         | 3         |
| 13  | 5         | 1         |
| 13  | 5         | 4         |
-------------------------------

我想得到的是“类型1”的表A中的所有对象,它们与之关联的对象的名称(否则为null)属于类型2, 即一个外部联接,用于获取类型1的所有对象,无论它们是否具有关联,但如果它们具有关联,则我需要该对象的名称。 请注意,类型1的对象将始终位于关联中的目标中。

以上示例的输出为

-------------------------------
| Target Name | Source Name   |
|-----------------------------|
| Name 1      | Name 2        |
| Name 3      | Name 2        |
| Name 4      | (NULL)        |
|-----------------------------|

我的原始连接查询(无法使外连接起作用)这是正常连接,不显示没有关联的对象。

select atrgt.NAME, asrc.NAME
from TABLE_A atrgt
JOIN TABLE_B trgttype on atrgt.TYPE_ID = trgttype.ID
         and trgttype.TYPE_NAME = 'Type 1'
JOIN TABLE_C assoc    on atrgt.ID = assoc.TARGET_ID
JOIN TABLE_A asrc     on asrc.ID = assoc.SOURCE_ID
JOIN TABLE_B srctype  on asrc.TYPE_ID = srctype.ID
         and srctype.TYPE_NAME = 'Type 2'

3 个答案:

答案 0 :(得分:6)

基本上在这些情况下我认为最好的方法是将查询细分为两个普通连接,然后在这些结果集之间进行外连接。如果您将SQL视为过程代码,您可能认为它看起来效率低下,但查询优化器不一定会独立运行这两个子连接。

您没有说出您正在使用的RDBMS。在Oracle中,我可能会这样写:

with
src_type_2 as (
  select c.target_id, a.name
    from table_c c
    join table_a on a.id = c.source_id
    join table_b on b.id = a.type_id
    where b.type_name = 'Type 2'
),
all_type_1 as (
  select a.id, a.name
  from table_a a
  join table_b on b.id = a.type_id
  where b.type_name = 'Type 1'
)
select tgt.name, src.name
  from all_type_1 tgt
  left join src_type_2 src on src.target_id = tgt.id

答案 1 :(得分:0)

尝试

select atrgt.NAME, baseview.NAME
from TABLE_A atrgt
JOIN TABLE_B trgttype on atrgt.TYPE_ID = trgttype.ID
         and trgttype.TYPE_NAME = 'Type 1'
JOIN TABLE_C assoc    on atrgt.ID = assoc.TARGET_ID
LEFT JOIN ( 
  TABLE_A asrc     on asrc.ID = assoc.SOURCE_ID
  JOIN TABLE_B srctype  on asrc.TYPE_ID = srctype.ID
         and srctype.TYPE_NAME = 'Type 2'
  ) as baseview

答案 2 :(得分:0)

我认为这应该有效:

SELECT
    TGT.NAME, SRC_TYPE.TYPE_NAME
FROM TABLE_A TGT
JOIN TABLE_B TGT_TYPE ON TGT.TYPE_ID = TGT_TYPE.ID
LEFT JOIN TABLE_C REL ON TGT.ID = REL.TARGET_ID
LEFT JOIN TABLE_A SRC ON REL.SOURCE_ID = SRC.ID
LEFT JOIN TABLE_B SRC_TYPE ON SRC_TYPE.ID = SRC.TYPE_ID
WHERE TGT_TYPE.TYPE_NAME = 'Type 1' AND COALESCE(SRC_TYPE.TYPE_NAME, 'Type 2') = 'Type 2'

如果您使用的是Oracle,则可以将COALESCE替换为NVL(SRC_TYPE.TYPE_NAME, 'Type 2')