【问题标题】:Excluding MYSQL query results with an INNER JOIN使用 INNER JOIN 排除 MYSQL 查询结果
【发布时间】: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 是您唯一关心的环境,那很好,但您应该在知情的情况下做出该决定,而不是默认。

标签: mysql sql select subquery


【解决方案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
      

      【讨论】:

      • 对于延迟接受您的回答深表歉意!在不得不搁置这个项目之后,我花了四个月的时间才终于再次回到这个项目,我没有意识到我把这个悬而未决。这是我采用的解决方案。它工作得很好,并且在潜在的巨大关系表上没有第二次查询。非常感谢您的帮助!
      猜你喜欢
      • 2021-12-26
      • 2011-12-22
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 2016-08-29
      • 1970-01-01
      • 2015-09-03
      相关资源
      最近更新 更多