【问题标题】:joining tables with not exists query [duplicate]加入不存在查询的表[重复]
【发布时间】:2012-06-27 23:43:45
【问题描述】:

可能重复:
Mysql: Perform of NOT EXISTS. Is it possible to improve permofance?

有没有更好/最佳的方法来做到这一点。我应该使用exists 而不是join 吗?还是两个单独的查询?那么临时表呢,因为我正在阅读那些但不确定的内容。

从群组中获取成员电子邮件。检查他们是否还没有收到物品。

SELECT m.email,g.id 
FROM group g 
  LEFT JOIN  members m 
    ON  g.mid = m.id 
    AND g.gid='1' 
WHERE NOT EXISTS 
      ( SELECT id 
        FROM items AS i 
        WHERE i.mid=m.id 
        AND i.item_id='5'
      ) 

【问题讨论】:

  • @Amit Bhargava - 不,因为我的是三个有连接的表。
  • 如果你只想要一个组的成员,为什么你有LEFT JOIN
  • @ypercube - 是的,这是正确的,但我也想要来自组的 id。 ty

标签: mysql left-join not-exists


【解决方案1】:

这里写成 JOIN 的东西是一样的:

SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id AND g.gid = '1' 
LEFT JOIN items i ON i.mid = m.id AND i.item_id = '5'
WHERE i.id IS NULL

使用以下复合索引:

group (mid, gid)
items (mid, item_id)

我反转了成员和组的 LEFT JOIN,因为您似乎返回的是成员,而不是组,并且我将 LEFT JOIN 更改为 INNER JOIN,因为您只需要该组的成员。

我认为这个可能更好读:

SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id
LEFT JOIN items i ON i.mid = m.id AND i.item_id = 5
WHERE g.gid = 1
  AND i.id IS NULL

您可能想知道我们是否也可以将i.item_id = 5 部分移动到 WHERE 子句中。你不能因为没有i.id IS NULLi.item_id = 5 所在的行。您必须先进行连接,然后消除 WHERE 子句中的 NULL 行。

【讨论】:

  • 是的,你是对的,加入应该是相反的,哈哈。关于 2 加入与 1 加入相对的问题并且存在,这是明智的表现方式。我问是因为此查询将在 cron 作业中运行。
  • @timeout,不存在is slower than JOIN,但最重要的部分是索引。
  • 有趣的阅读,谢谢
  • 我不相信第二个查询(将i.item_id = 5 上的谓词移至WHERE 子句将返回指定的结果集。该谓词需要在ON 子句中。 (我在此为未来的读者记下这一点,因为这是“选择”的答案。)
  • @spencer7593 - 你和 Marcus 返回相同的结果。 :) 泰
【解决方案2】:

我认为不需要临时表。如果我们无法获得可接受的性能,我们真的只会走这条路。

根据您的查询,我们收集到您的架构如下所示:

group (id INT PK, gid INT, mid INT)
items (id INT PK, item_id INT, mid INT)
members (id INT PK, email VARCHAR)

看起来您的group 表实际上是一个“成员”表,它解决/实现了组和人之间的多对多关系。 (也就是说,一个人可以是零个、一个或多个组的成员;一个组可以有零个或多个人作为成员。)

您在groupmembers 之间使用左连接。当没有匹配的成员时,这将为组返回一行(返回 group.id),members.email 为 NULL(这可能是您想要的)。但是,如果您只想返回电子邮件地址,则可以将其更改为 INNER JOIN。

NOT EXISTS 谓词可以替换为 OUTER JOIN 并测试从 JOINED 表返回的 NULL 值。如果 group.gid 和/或 items.item_id 列是数字数据类型,那么您可以删除谓词中整数文字周围的引号。

这是一个替代方案,它会返回一个等效的结果集,并且可能会表现得更好:

SELECT m.email
     , g.id 
  FROM members m
  JOIN group g ON g.mid = m.id AND g.gid = 1
  LEFT
  JOIN items i ON i.mid = m.id AND i.item_id = 5
 WHERE i.id IS NULL

附录:

TEST CASE(在所选答案的注释中提供)演示了在 ON 子句和 WHERE 子句中使用谓词 items.item_id = 5 的查询之间结果集的差异。 (将这个谓词移动到 WHERE 子句会与反连接混淆。)

CREATE TABLE `group` (`id` INT PRIMARY KEY, `gid` INT, `mid` INT);
CREATE TABLE `items` (`id` INT PRIMARY KEY, `item_id` INT, `mid` INT);
CREATE TABLE `members` (`id` INT PRIMARY KEY, `email` VARCHAR(40));

INSERT INTO `group` VALUES (1,1,1), (2,1,2);
INSERT INTO `items` VALUES (1,5,1);
INSERT INTO `members` VALUES (1,'one@m.com'),(2,'two@m.com'); 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-15
    • 2019-08-17
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 2013-03-31
    • 2014-06-01
    • 2015-12-21
    相关资源
    最近更新 更多