【问题标题】:Sql multiple joins to same table both inclusive and exclusiveSql 多个连接到同一个表,包括和独占
【发布时间】:2013-02-16 02:34:01
【问题描述】:

我有一张桌子 Foo

FOO
-------
id
name

BAR
-------
id
name

FOO_BAR_XREF
-------
foo_id
bar_id

我需要选择 foo.id 在 foo_bar_xref 中有一条 bar_id 为 1 的记录的所有 FOO 实例,并从 foo 中排除 foo.id 有一条记录为 n foo_bar_xref 且 bar_id 为 2 的记录

foo -> foo_bar_xref 是一对多 *强调文本*foo_bar_xref 可以包含每个 foo_id 的多个 bar_id

这是否可能只使用连接,还是我需要在 where 子句中使用 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.

那不是过滤掉 bar 为 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

【问题讨论】:

  • 我喜欢你的左连接方法——我的想法也是如此。

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


【解决方案1】:

重申我的评论,我认为 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

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

【讨论】:

    【解决方案2】:

    我需要选择 foo.id 有记录的所有 FOO 实例 bar_id 为 1 的 foo_bar_xref 并从 foo 中排除记录 foo.id 有一条记录 n foo_bar_xref,bar_id 为 2

    由于您使用的是 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;
    

    【讨论】:

      【解决方案3】:

      以下是执行此操作的一种方法。它总结了外部参照表以获取 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 未经测试,因此可能存在语法错误。

      【讨论】:

      • 子选择是否比我工作的内/左连接组合效率更高或更低?
      • @dstarh 。 . .真正回答这个问题的唯一方法是比较实际的执行计划。此方法扫描外部参照表,将其分组(可能使用散列组算法),然后加入 foo。您的方法需要两个连接到较大的表,然后是 group by。这种方法很可能更快。
      【解决方案4】:
      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
      

      【讨论】:

        猜你喜欢
        • 2011-06-15
        • 2018-12-19
        • 1970-01-01
        • 1970-01-01
        • 2018-07-06
        • 2012-04-12
        • 2013-05-11
        • 1970-01-01
        相关资源
        最近更新 更多