【问题标题】:SQL JOIN with negative WHERE condition带有否定 WHERE 条件的 SQL JOIN
【发布时间】:2020-03-23 22:02:35
【问题描述】:

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

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',...)

然后它不起作用。无论如何,将返回具有多个标签且其中一个标签在列表中的所有文档。因为带有 Document 和那些剩余标签(未列出的标签)组合的 raw 将简单地匹配 where 条件,因此查询将返回我不想返回的 Documents。

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

顺便说一下,为了简单起见,我们可以用“!=”替换“不在”。问题还是一样。

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

【问题讨论】:

    标签: sql join sql-in


    【解决方案1】:

    您可以使用LEFT JOIN 来选择所有不匹配的行:

    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

    【讨论】:

    • 我认为只有 NOT IN 选项是正确的。前 2 个可以返回不需要的 ID。一个 ID 可以有 label1、label2,也可以有 label3。由于 label3 将返回 ID,但实际上不应返回。
    • 如果表 label 中的 document_id 具有任何值('label1'、'label2'、...),则 exists 将返回 truenot exists 将返回false 并且该 id 不会出现在结果中。同样适用于 LEFT JOIN。
    • 是的,我现在又“重新编译”了它。我的错,我误解了前两个,我对NOT IN 解决方案的影响太大了:)
    • 完成。我遇到的最后一个问题是我不知道如何在 JPA Criteria API 中执行此操作。但这是不同的主题
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      如果您想接收没有任何给定标签的文档,那么您可以使用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', ...)
          )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-03
        相关资源
        最近更新 更多