多个多对多双向自内连接,无需重复整个查询

时间:2014-06-12 21:29:37

标签: sql oracle postgresql

我有一个数据模型,使得项目可以与同一个表中的其他项目具有多对多关系,使用第二个表来定义关系。让我们调用主要表items,键入item_id,关系表item_assoc调用列item_idother_item_id以及assoc_type。通常,您可以使用union来获取可能在item_assoc表中的任意一个方向上定义的关系,但您最终会重复查找相同查询的其他部分以确保选择在任一方向定义的协会。

假设您正在尝试将类似于以下内容的相当复杂的查询放在一起,您希望找到包含可能具有关联取消项目的相关项目的项目列表,但选择那些没有取消项目的项目:

select 
  orig.*
from items as orig

join item_assoc as orig2related
  on orig.item_id = orig2related.item_id
join items as related
  on orig2related.other_item_id = related.item_id
 and orig2related.assoc_type = 'Related'

left join item_assoc as related2cancel
  on related.item_id = related2cancel.item_id
left join items as cancel
  on related2cancel.other_item_id = cancel.item_id
 and related2cancel.assoc_type = 'Cancellation'

where cancel.item_id is null

此查询显然只选取其关系在一个方向上定义的项目。对于不太复杂的查询,我可以通过在底部为反向关系的每个排列添加union来解决此问题,但我认为这会使查询不必要地长且难以理解。

有没有办法可以定义每个关系的两个方向而不重复查询的其他部分?

2 个答案:

答案 0 :(得分:2)

item_assoc中的UNION可以提供帮助。假设您有一个没有WITH子句的DB,则必须定义一个视图

CREATE VIEW bidirec_item_assoc AS
(
SELECT item_id, other_item_id, assoc_type, 1 as direction FROM item_assoc
UNION
SELECT other_item_id, item_id, assoc_type, 2 as direction FROM item_assoc
)

现在,您可以在之前使用bidirec_item_assoc的查询中使用items_assoc

已编辑: 您可以添加方向和关系类型的列,当然

答案 1 :(得分:1)

简化,简化和简化:不要在查询中涉及不需要的表。

以下查询应与您的示例查询等效,并且更能表达您的意图:

select i.*
from items i
where not exists ( select *
                   from item_assoc r
                   join item_assoc c on c.item_id    = r.item_id
                                    and c.assoc_type = 'Cancellation'
                   where r.item_id    = i.item_id
                     and r.assoc_type = 'Related'
                 )

它应该选择与已取消的项目无关的项目集。不需要3次加入项目表。

此外,您的原始查询将有重复的行:第一个项目表(orig)中的每一行将为每个相关项目重复一次。