【问题标题】:How do I replace NOT EXISTS with JOIN?如何用 JOIN 替换 NOT EXISTS?
【发布时间】:2014-06-10 15:15:41
【问题描述】:

我有以下查询:

select distinct a.id, a.name
from Employee a
join Dependencies b on a.id = b.eid
where not exists 
    ( 
select * 
    from Dependencies d 
    where b.id = d.id 
    and d.name  = 'Apple'
    )
and exists 
    (
    select * 
    from Dependencies c 
    where b.id = c.id 
    and c.name  = 'Orange'
    );

我有两张桌子,比较简单。 第一个 Employee 有一个 id 列和一个 name 列 第二个表 Dependencies 有 3 列,一个 id、一个 eid(要链接的员工 id)和名称(apple、orange 等)。

数据看起来像这样 员工表是这样的

id  | name
-----------
1   | Pat
2   | Tom
3   | Rob
4   | Sam

依赖关系

id  | eid | Name
--------------------
1   | 1   |  Orange
2   | 1   |  Apple
3   | 2   |  Strawberry
4   | 2   |  Apple
5   | 3   |  Orange
6   | 3   |  Banana

如您所见,Pat 同时拥有 Orange 和 Apple,他需要被排除在外,并且必须通过连接,而我似乎无法让它工作。最终数据应该只返回 Rob

【问题讨论】:

  • 用简单的英语写下您的要求。我必须阅读您的代码才能尝试猜测您的要求。

标签: mysql sql join output


【解决方案1】:

inner join 用你想要的名字,left join 用你不想要的名字,然后用 where 确保 left join 不匹配,像这样(SQL Fiddle):

select distinct a.id, a.name
from Employee a
  inner join Dependencies b on a.id = b.eid
    and b.name = 'Orange'
  left join Dependencies c on ( a.id = c.eid
    and c.name = 'Apple')
where c.id is null;

【讨论】:

  • 是否可以在具有各种别名的依赖项上进行多个联接?我不确定 c.id is null 是什么意思?
  • 是的,您最终会在查询计划中得到一个表(或可能的索引)假脱机(当一个表被使用两次时的冗余操作,但您之前使用过 3 次),但是该操作是完全合法的。至于 id 为 null - 当条件不匹配时,左连接用 null 填充 C 的列,而不是消除行。因此,当连接条件失败时,c.id 为空。去查找不同类型的连接之间的区别。希望这会有所帮助。
  • @YelizavetaYR 所以这样写... "SELECT DISTINCT a.id a_id,a.name,c.id c_id from... where 1=1"
  • 附带说明:select a.x from a left join b on a.x = b.x where b.x is null 是 MySQL 中有时用来模拟 minus 运算符的习语,而 MySQL 中没有实现。
  • @stawberry 请不要鼓励 1=1 废话。没有理由将它添加到查询中。
【解决方案2】:

将需要两个连接到 Dependencies,因为有 2 个测试。暂时忽略性能,您可以尝试通过命名别名来提高连接的可理解性,例如:

SELECT DISTINCT e.ID, e.Name
   FROM Employee e
   LEFT OUTER JOIN Dependencies withApple
      ON withApple.eid = e.id
      AND withApple.Name = 'Apple'
   LEFT OUTER JOIN Dependencies withOrange
      ON withOrange.eid = e.id
      AND withOrange.Name = 'Orange'
   WHERE
      withApple.id IS NULL -- Don't want this
      AND
      withOrange.id IS NOT NULL -- Do want this.

SqlFiddle

【讨论】:

  • 虽然有效,但我认为它实际上更糟。仅当您必须处理丢失的行或想要计算差异时,使用 left outer join 才有意义。由于id 是您加入的列,因此条件id is not null 毫无意义。此外,cmets“想要这个”和“不想要这个”也无济于事。他们不应该反过来吗?视情况而定,它只是证明了这一点。
  • 你是对的 - 别名的命名抢占了过滤并传达了令人困惑的信息。与 IJ 相反,LOJ 背后的想法是尝试将过滤延迟到WHERE 子句,并赋予不想要的苹果与想要的橙色相同的对称性。但你是对的,这不是好的 Sql,其意图只是一个临时的垫脚石,希望能点击 OP 理解中的某些内容。
  • 是的,具有讽刺意味的是,在我看来,带有 exists subqueries 的原始查询(除了无用的连接)比任何带有 joins的建议解决方案都更好地传达了意图>,所以我什至不会尝试改变它并保持原样。 :)
猜你喜欢
  • 2018-01-13
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 2017-01-08
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
相关资源
最近更新 更多