MySQL左外连接,排除属于用户的第二个表中的项目

And*_*w M 9 mysql join left-join

我的MySQL数据库中有两个表,一个是数据库中所有书籍的库,另一个是包含对应于用户库中书籍的各个行.

例如:

图书馆表

 `id`        `title`...
=====        ===========
  1          Moby Dick
  2          Harry Potter
Run Code Online (Sandbox Code Playgroud)

收集表

 `id`        `user`      `book`
=====        ======      =======
  1            1            2
  2            2            2
  3            1            1
Run Code Online (Sandbox Code Playgroud)

我想要做的是运行一个查询,显示所有不在用户集合中的书籍.我可以运行此查询来显示不在任何用户集合中的所有书籍:

SELECT * 
FROM  `library` 
LEFT OUTER JOIN  `collection` ON  `library`.`id` =  `collection`.`book` 
WHERE  `collection`.`book` IS NULL
Run Code Online (Sandbox Code Playgroud)

据我所知,这很好用.在PHPMyAdmin中运行此操作将导致所有不在集合表中的书籍.

但是,如何将其限制为某个用户?例如,使用上面的虚拟数据,如果用户2运行查询,我想要书1,如果用户运行查询,则不需要书1.

只是添加一个AND user=[id]不起作用,并且由于我对JOIN语句的知识非常有限,我实际上并没有得到任何结论.

此外,返回的结果的ID(显示的查询,不执行我想要但功能正常)是0 - 如何确保返回的ID是library.id

Zan*_*ien 13

您必须将您的LEFT JOIN选择范围缩小到只有特定用户拥有的书籍,然后NULL连接表格中的任何内容都将是用户在其收藏中没有的行(书籍):

SELECT
    a.id,
    a.title
FROM
    library a
LEFT JOIN
    (
        SELECT book
        FROM collection
        WHERE user = <userid>
    ) b ON a.id = b.book
WHERE 
    b.book IS NULL
Run Code Online (Sandbox Code Playgroud)

另一种选择是:

SELECT
    a.id,
    a.title
FROM
    library a
WHERE 
    a.id NOT IN
    (
        SELECT book
        FROM collection
        WHERE user = <userid>
    )
Run Code Online (Sandbox Code Playgroud)

但是,第一个解决方案更为优化,因为MySQL将为NOT IN每一行执行一次子查询,而不是仅对整个查询执行一次.直观地说,你会期望MySQL一次执行子查询并将其用作列表,但MySQL并不够聪明,无法区分相关子查询和非相关子查询.

如前所述这里:

"问题在于,对于使用IN子查询的语句,优化器会将其重写为相关子查询."

  • 你的最后一段指向一个链接,其中提到了`IN`子查询,而不是'NOT IN`子查询.所以,你所暗示的是,"NOT IN"的查询没有得到很好的优化是错误的.看到这篇伟大的博客帖子:[NOT IN vs. NOT EXISTS vs. LEFT JOIN/IS NULL:MySQL](http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-左联接,是无效的mysql /) (2认同)