【问题标题】:INNER JOIN where **every** row must match the WHERE clause?INNER JOIN where **every** 行必须匹配 WHERE 子句?
【发布时间】:2015-09-17 19:10:25
【问题描述】:

这是我正在尝试做的一个简化示例。我有两张桌子,A 和 B。

A          B
-----      -----
id         id
name       a_id
           value

我只想选择 A 中的行,其中 B 中的所有行的值都与 where 子句匹配。比如:

SELECT * from A INNER JOIN B on B.a_id = A.id WHERE B.value > 2

上述查询的问题是,如果 B 中的 ANY 行的值 > 2,我将从 A 中获取相应的行,并且我只想要 A 中的行,如果

1.) ALL B 中的行 for B.a_id = A.id 匹配 WHERE,OR

2.) B 中没有引用 A 的行

B 基本上是一个过滤器表。

【问题讨论】:

  • 您能否编辑您的问题并提供示例数据和所需的结果?
  • 你的定义是矛盾的。如果a.id 中有多个不同的值,ALL the rows in B match the WHERE不可能的,因为B 中的一行一次不能有多个值。请说清楚。您的意思似乎是:返回来自A 的所有行,其中B 中的所有行匹配B.a_id = A.id 也满足第二个条件B.value > 2
  • 你说得对,@ErwinBrandstetter,我想到了 B.a_id = A.id 的约束。编辑澄清。

标签: sql postgresql left-join exists


【解决方案1】:

回答这个问题(您似乎真的想问这个问题):

返回来自A 的所有行,其中BB.a_id = A.id 中的所有行也通过了测试B.value > 2

相当于:

返回来自A 的所有行,其中B 中没有带有B.a_id = A.id 的行未通过测试B.value > 2

SELECT a.*  --  "rows from A" (so don't include other columns)
FROM   a
LEFT   JOIN b ON b.a_id = a.id
             AND (b.value > 2) IS NOT TRUE -- safe inversion of logic
WHERE  b.a_id IS NULL;

当反转 WHERE 条件时,请仔细考虑 NULL。 IS NOT TRUE 是完美反转WHERE 条件的简单且安全的方法。替代方案是 (b.value <= 2 OR b.value IS NULL),它更长但可能更快(更容易支持索引)。

【讨论】:

    【解决方案2】:

    这应该可以解决您的问题:

    SELECT *
    FROM   a
    WHERE  NOT EXISTS (SELECT *
                       FROM   b
                       WHERE  b.a_id = a.id
                       AND    b.value <= 2)
    

    这是获得它的方式。

    假设我们有一个通用量词(平行于 EXISTS,存在量词),其语法如下:

    FORALL table WHERE condition1 : condition2
    

    (待读:FORALL表中满足条件1的元素,则条件2为真)

    所以你可以这样写你的查询:

    SELECT *
    FROM   a
    WHERE  FORALL b WHERE b.a_id = a.id : b.value > 2
    

    (请注意,即使 b 中不存在值为 a.id 的元素,forall 也为真)

    然后我们可以像往常一样用双重否定将全称量词转换为存在量词:

    SELECT *
    FROM   a
    WHERE  NOT EXISTS b WHERE b.a_id = a.id : NOT (b.value > 2)
    

    在普通 SQL 中,这可以写成:

    SELECT *
    FROM a
    WHERE NOT EXISTS (SELECT * 
                      FROM   b 
                      WHERE  b.a_id = a.id
                      AND    (b.value > 2) IS NOT TRUE)          
    

    这种技术在普遍量化的情况下非常方便。

    【讨论】:

    • 只考虑NULL。
    【解决方案3】:
    SELECT * FROM A LEFT JOIN B ON b.a_id = a.id
    WHERE B.a_id IS NULL OR NOT EXIST (
            SELECT  1
            FROM    b
            WHERE  b.value <= 2) 
    

    【讨论】:

    • 这是无效的语法(无连接条件)。
    • 现在它的逻辑是无效的 :) 任何带有b.value = 1 的单行都会从 A 中删除所有在 b.a_id = a.id 上与 B 中 any 匹配的行。
    • 1.) B 中的所有行都与 WHERE 匹配,或者(在这种情况下,如果 b.value = 1 则并非 B 中的所有行都与 where 匹配)因此它将尝试使第二种情况为真 2.) B 中没有引用 A 的行,..... 没有引用 A 的行意味着 B.a_id 为空。这就是问题的要求!!
    【解决方案4】:

    如果您不想使用EXISTS,您可以使用外连接。

    SELECT A.* 
    FROM 
        A 
        LEFT JOIN B ON 
            B.a_id = A.id 
            AND B.value <= 2 -- note: condition reversed!!
    WHERE B.id IS NULL
    

    这通过搜索B 中是否存在失败记录来工作。如果找到,则连接将匹配,最后的 WHERE 子句将排除该记录。

    【讨论】:

    • 只考虑NULL。
    【解决方案5】:
    SELECT a.is, a.name, c.id as B_id, c.value from A 
    INNER JOIN (Select b.id, b.a_id, b.value from B WHERE B.value > 2) C
    on C.a_id = A.id 
    

    请注意,使用 select * 是一种不好的做法。您应该只指定您需要的字段。在这种情况下,我可能会删除 b.Id 引用,因为它们可能不需要。如果您有一个连接,则有 100% 的机会浪费资源发送您不需要的数据,因为连接字段将重复。这就是为什么我没有在最终结果集中包含 a_id。

    【讨论】:

      【解决方案6】:

      试试这个

      SELECT * FROM A 
      LEFT JOIN B ON B.a_id = A.id
      WHERE B.value > 2 OR B.a_id IS NULL
      

      【讨论】:

      • 如果B 中只有一个条目匹配a_id 的过滤器,这将返回一行,而不管其他条目不匹配
      • 此查询应返回 A 中不存在于 B 中且值 > 2 的所有记录,这是 OP 所要求的
      • 操作要求所有 B 中的行与WHERE 匹配。如果B 中的any 行与WHERE 匹配,或者B 中没有与此a_id 对应的行,则您的查询将返回。
      • 我不确定我是否误解了这 2 个条件“1.) B 中的所有行都与 WHERE 匹配,或者 2)B 中没有引用 A 的行”
      • 2)There are no rows in B that reference A 告诉我,如果 B 中没有匹配项,他想要 B 中的所有行 + 任何在 B 中匹配但值 > 2 的内容,如果这不是 OP请求我建议他将 jFiddle 与虚拟数据放在一起,以便我们知道要推荐什么
      【解决方案7】:
      SELECT  *
      FROM    a
      WHERE   NOT EXISTS
              (
              SELECT  NULL
              FROM    b
              WHERE   b.a_id = a.a_id
                      AND (b.value <= 2 OR b.value IS NULL)
              )
      

      【讨论】:

      • 如果你选择使用EXISTS逻辑而不是连接SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.a_id = a.a_idAND (IFNULL(b.value, 0) &lt;= 2),也可以这样写查询
      猜你喜欢
      • 1970-01-01
      • 2013-08-11
      • 2010-11-04
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      • 2019-01-15
      相关资源
      最近更新 更多