2013-01-14 32 views
2

我有一个表美孚的Sql多个连接到同一台既包容性和排他性

FOO 
------- 
id 
name 

BAR 
------- 
id 
name 

FOO_BAR_XREF 
------- 
foo_id 
bar_id 

,我需要选择FOO的所有实例,其中foo.id在foo_bar_xref记录为1的bar_id和排除记录从foo其中foo.id有2

FOO的bar_id一个结果N foo_bar_xref - > foo_bar_xref是一对多*强调文本* foo_bar_xref可能包含每foo_id多个bar_id的

这是可能的,只有加入或我需要在w中使用not exists语句吗?这里的条款?

到目前为止,我

select f.name from FOO f 
inner join FOO_BAR_XREF fb_1 on fb_1.foo_id = f.id 
inner join FOO_BAR_XREF fb_2 on fb_2.foo_id = f.id 

where fb_1.bar_id = 1 and fb_2.bar_id <> 2 
group by f.name -- remove dupes - running on sql server and is paged with sql servers hackish over keyword where distinct doesn't work so well. 

那不是筛选出有2

这似乎是工作一个酒吧,但我不知道它是最有效的Foo的

select f.name from FOO f 
inner join FOO_BAR_XREF fb_1 on fb_1.foo_id = f.id and fb_1.bar_id = 1 
left outer join FOO_BAR_XREF fb_2 on fb_2.foo_id = f.id and fb_2.bar_id = 2 

where fb_2.bar_id is null 
group by f.name -- remove dupes 
+0

我喜欢你的左连接方式 - 我的想法也是如此。 – sgeddes

回答

3

要重申我的意见,我觉得OP具有最佳的解决方案。我一直在使用左连接来完成这些场景。

select f.name 
from FOO f 
inner join FOO_BAR_XREF fb_1 
    on fb_1.foo_id = f.id and fb_1.bar_id = 1 
left outer join FOO_BAR_XREF fb_2 
    on fb_2.foo_id = f.id 
    and fb_2.bar_id = 2 
where fb_2.bar_id is null 
group by f.name 

SQL Fiddle

顺便说一句,我认为这是关于表扫描最有效的解决方案。检查你的执行计划,看看你有什么进展。

1

以下是执行此操作的一种方法。它汇总了外部参照表,以获得bar = 1但不是bar = 2的foo。一般来说,我更喜欢使用聚合来处理这种类型的查询。

select f.* 
from foo f join 
    (select f.foo_id 
     from foo_bar_xref 
     group by f.foo_id 
     having max(case when bar_id = 1 then 1 else 0 end) > 0 and -- has 1 bar 
      max(case when bar_id = 2 then 1 else 0 end) = 0  -- no 2 bar 
    ) fbx 
    on f.id = fbx.foo_id 

注意:此SQL未经测试,因此可能有语法错误。

+0

是否比我正在工作的inner/left join组合有更高或更低效率? – dstarh

+0

@dstarh。 。 。真正回答这个问题的唯一方法是比较实际的执行计划。此方法扫描外部参照表,将其分组(可能使用哈希组算法),然后加入到foo中。你的方法需要两个连接到较大的表,然后是一个分组。这种方法很可能会更快。 –

2

,我需要选择FOO的所有实例,其中foo.id在 foo_bar_xref记录为1的bar_id从foo其中 foo.id有2

一个bar_id一个结果N foo_bar_xref排除记录

由于您使用的是SQL Server,您可以使用EXCEPT设置操作这样做是这样的:

SELECT f.id, f.name 
FROM FOO f 
INNER JOIN FOO_BAR_XREF fb_1 ON fb_1.foo_id = f.id 
WHERE fb_1.bar_id = 1 
EXCEPT 
SELECT f.id, f.name from FOO f 
INNER JOIN FOO_BAR_XREF fb_1 ON fb_1.foo_id = f.id 
WHERE fb_1.bar_id = 2; 
0
select * from foo join (
select foo_id from FOO_BAR_XREF where bar_id =1 
except 
select foo_id from FOO_BAR_XREF where bar_id =2 
) X on id=foo_id