使用 INNER JOIN 排除 MYSQL 查询结果

Posted

技术标签:

【中文标题】使用 INNER JOIN 排除 MYSQL 查询结果【英文标题】:Excluding MYSQL query results with an INNER JOIN 【发布时间】:2016-01-02 12:41:18 【问题描述】:

我有两张桌子。第一个充满了书籍,每本书都有一个book_id。第二个表是book_idkeyword_id 的关系表。

SELECT b.* FROM books_table b 
INNER JOIN keywords_table k 
ON b.book_id = k.book_id AND k.keyword_id NOT IN(1,2,3)
WHERE b.is_hardcover = 1 
GROUP BY b.book_id

期望的结果

keyword_id 为 1、2 或 3 的书籍均未附加到任何书籍。

实际结果

书籍可以有关键字 1、2 或 3,只要它们附加了排除列表中的附加关键字 ID。

我试过的

上面的查询是我最接近实现它的方法,但在这方面它失败了。

我怎样才能以最优化的方式达到预期的结果?

【问题讨论】:

希望得到“没有...的书”的结果会让自己和他人感到困惑。 请注意,选择除分组列和组的聚合函数之外的列不是有效的 SQL。它对你有用,因为 mysql 支持它作为扩展。如果 MySQL 是您唯一关心的环境,那很好,但您应该在知情的情况下做出该决定,而不是默认。 【参考方案1】:

您可以使用以下查询:

SELECT *
FROM books_table
WHERE is_hardcover = 1 AND
      book_id NOT IN (SELECT book_id
                      FROM keywords_table
                      GROUP BY book_id
                      HAVING COUNT(CASE WHEN keyword_id IN (1,2,3) THEN 1 END) <> 0)

Demo here

【讨论】:

这似乎会漏掉根本没有指定关键字的书籍。 @JohnBollinger 是的。我进行了修改以纠正问题。【参考方案2】:

您要求的是“反加入”的味道。有几种方法可以实现它;这是一个:

SELECT b.* FROM books_table b 
LEFT JOIN keywords_table k 
  ON b.book_id = k.book_id AND k.keyword_id IN (1,2,3)
WHERE k.book_id IS NULL AND b.is_hardcover = 1 

左连接将左表 (books_table) 中的每一行与右表中满足条件 b.book_id = k.book_id AND k.keyword_id IN (1,2,3) 的行匹配,并且 每行包含一个结果行与右表的任何行都不匹配的左表。过滤条件k.book_id IS NULL 与连接条件冲突,因此只能由左行产生的行与右行不匹配。

请注意,将条件分配给连接谓词和过滤谓词对于像这样的外部连接至关重要。另请注意,在这种情况下不需要GROUP BY 子句,除非books_table 可能包含重复的book_ids。

这种方法在实践中可能比基于WHERE 子句中的相关子查询的方法执行得更好。但是,如果性能很重要,那么建议您测试正在考虑的替代方案。

【讨论】:

【参考方案3】:

如您所述,此查询将生成至少有一个关键字不是 1、2 或 3 的任何书籍,这不是您想要的。相反,您希望明确排除带有这些关键字的书籍。 join 并不是真正适合这里的工作。相反,您可以使用 exists 运算符:

SELECT b.* 
FROM   books_table b 
WHERE  b.is_hardcover = 1 AND
       NOT EXISTS (SELECT * 
                   FROM   keywords_table k 
                   WHERE  b.book_id = k.book_id AND 
                          k.keyword_id IN (1,2,3))

【讨论】:

【参考方案4】:

你可以这样做

SELECT b.* 
FROM books_table b 
INNER JOIN keywords_table k 
ON b.book_id = k.book_id
WHERE b.is_hardcover = 1 
GROUP BY b.book_id
HAVING SUM(k.keyword_id = 1) =0
AND SUM(k.keyword_id = 2) =0
AND SUM(k.keyword_id = 3) =0

【讨论】:

对于延迟接受您的回答深表歉意!在不得不搁置这个项目之后,我花了四个月的时间才终于再次回到这个项目,我没有意识到我把这个悬而未决。这是我采用的解决方案。它工作得很好,并且在潜在的巨大关系表上没有第二次查询。非常感谢您的帮助!

以上是关于使用 INNER JOIN 排除 MYSQL 查询结果的主要内容,如果未能解决你的问题,请参考以下文章

使用昂贵的 INNER JOIN 优化 MySQL 查询

Mysql 多表连接查询 inner join 和 outer join 的使用

Mysql 连接(left join, right join, inner join ,full join)

mysql的unionleft join right join inner join和视图学习

即使使用 INNER JOIN 而不是 IN,MySQL 查询也非常慢

使用 ORDER BY 和 INNER JOIN 优化 MySQL 查询(选择用户关注的位置)