SQL 具有否定 WHERE 条件的 JOIN

SQL JOIN with negative WHERE condition

我有两个表 Document 和 Label(不是我的真实情况,我在类比)。一个文档可以有 N 个标签。当我需要 select 列出标签的文档时,我可以轻松做到这一点

select D.id from document D
join label L on  D.id = L.document_id
where L.value in('label1','label2',...)

如何在需要没有列出标签的文档时编写查询? 当我这样做时

select D.id from document D
join label L on  D.id = L.document_id
where L.value not in('label1','label2',...)

那就不行了。无论如何,所有具有多个标签且其中一个标签在列表中的文档都将被 returned。因为结合了 Document 和那些剩余标签(未列出的标签)的 raws 将简单地匹配 where 条件,所以查询将 return 我不想被 returned 的文档。

我实际上正在处理 Java Spring JPA 类型查询中的查询。我需要为我的过滤框架解决这个问题。但我认为最好先在 SQL 级别解决这个问题。

顺便说一句,为了简单起见,我们可以将 "not in" 替换为“!=”。问题还是一样

有什么简单的解决方案吗?提前谢谢你

您可以使用 LEFT JOIN 来做到这一点,您 select 所有不匹配的行:

select D.id 
from document D left join label L 
on D.id = L.document_id and L.value in('label1','label2',...)
where L.document_id is null

NOT EXISTS:

select D.id from document D 
where not exists (
  select 1 from label L
  where L.document_id = D.id and L.value in('label1','label2',...)
)

NOT IN:

select id from document
where id not in (
  select document_id from label 
  where value in('label1','label2',...)
)

查看简化版 demo.

如果您要查找没有标签的文档,则需要外部联接

select D.id 
from document D
left join label L on  D.id = L.document_id
where L.value is null

如果您想要接收没有任何给定标签的文档,那么您可以使用 not in 排除您将通过已有的肯定查询获得的 ID。 如果您想在搜索其他标签时排除某些标签,您可以使用以下方法将两者结合起来:

    where id not in (
      select D.id from document D
      join label L on  D.id = L.document_id
      where L.value in('label1', ...)
    ) and id in (
      select D.id from document D
      join label L on  D.id = L.document_id
      where L.value in('label2', ...)
    )