【问题标题】:MySQL Join Where Not ExistsMySQL 加入不存在的地方
【发布时间】:2010-10-19 12:25:57
【问题描述】:

我有一个连接两个表的 MySQL 查询

  • 选民
  • 家庭

他们加入 voters.household_idhousehold.id

现在我需要做的是修改它,将选民表与voter.idelimination.voter_id 连接到第三个名为消除的表。但是问题是我想排除在淘汰表中有相应记录的选民表中的任何记录。

我如何设计一个查询来做到这一点?

这是我当前的查询:

SELECT `voter`.`ID`, `voter`.`Last_Name`, `voter`.`First_Name`,
       `voter`.`Middle_Name`, `voter`.`Age`, `voter`.`Sex`,
       `voter`.`Party`, `voter`.`Demo`, `voter`.`PV`,
       `household`.`Address`, `household`.`City`, `household`.`Zip`
FROM (`voter`)
JOIN `household` ON `voter`.`House_ID`=`household`.`id`
WHERE `CT` = '5'
AND `Precnum` = 'CTY3'
AND  `Last_Name`  LIKE '%Cumbee%'
AND  `First_Name`  LIKE '%John%'
ORDER BY `Last_Name` ASC
LIMIT 30 

【问题讨论】:

    标签: mysql join not-exists


    【解决方案1】:

    我可能会使用LEFT JOIN,即使没有匹配也会返回行,然后您可以通过检查NULLs 仅选择不匹配的行。

    所以,类似:

    SELECT V.*
    FROM voter V LEFT JOIN elimination E ON V.id = E.voter_id
    WHERE E.voter_id IS NULL
    

    这是否比使用子查询效率更高或更低取决于优化、索引、每个投票者是否可能有多个淘汰等。

    【讨论】:

    • +1 在高负载下比子查询快得多 + 如果 U 可以执行 JOIN 而不是子查询 - 只需执行 JOIN,它们对于分析器来说更简单。另一个有用的示例,如果右表中有一些行或没有行,U 可能希望获得结果:SELECT V.* FROM voter V LEFT JOIN elimination E ON V.id = E.voter_id OR E.voter_id IS NULL 例如:如果 U 不想将所有记录存储在右表中的每一行左边。
    • E.voter_id 可以是我们JOIN 来自的数据集中的NULL 时,您将如何修改此查询以查找E 中不存在的行?
    • 您需要将表格与一些公共列或相关值链接在一起。但我认为这可行(未经测试):SELECT V.*, COUNT(E.*) AS `countE` FROM voter V LEFT JOIN elimination E ON V.id = E.voter_id WHERE countE = 0;
    【解决方案2】:

    我会使用“不存在的地方”——正如你在标题中建议的那样:

    SELECT `voter`.`ID`, `voter`.`Last_Name`, `voter`.`First_Name`,
           `voter`.`Middle_Name`, `voter`.`Age`, `voter`.`Sex`,
           `voter`.`Party`, `voter`.`Demo`, `voter`.`PV`,
           `household`.`Address`, `household`.`City`, `household`.`Zip`
    FROM (`voter`)
    JOIN `household` ON `voter`.`House_ID`=`household`.`id`
    WHERE `CT` = '5'
    AND `Precnum` = 'CTY3'
    AND  `Last_Name`  LIKE '%Cumbee%'
    AND  `First_Name`  LIKE '%John%'
    
    AND NOT EXISTS (
      SELECT * FROM `elimination`
       WHERE `elimination`.`voter_id` = `voter`.`ID`
    )
    
    ORDER BY `Last_Name` ASC
    LIMIT 30
    

    这可能比执行左连接稍微快一些(当然,取决于您的索引、表的基数等),并且几乎可以肯定比使用 IN 快很多

    【讨论】:

    • 谢谢你 - 对我来说肯定更快。
    【解决方案3】:

    有三种可能的方法来做到这一点。

    1. 选项

      SELECT  lt.* FROM    table_left lt
      LEFT JOIN
          table_right rt
      ON      rt.value = lt.value
      WHERE   rt.value IS NULL
      
    2. 选项

      SELECT  lt.* FROM    table_left lt
      WHERE   lt.value NOT IN
      (
      SELECT  value
      FROM    table_right rt
      )
      
    3. 选项

      SELECT  lt.* FROM    table_left lt
      WHERE   NOT EXISTS
      (
      SELECT  NULL
      FROM    table_right rt
      WHERE   rt.value = lt.value
      )
      

    【讨论】:

      【解决方案4】:

      警惕“左”连接 - 左连接本质上是外连接。不同的 RDBMS 查询解析器和优化器可能会非常不同地处理 OUTER JOINS。以 MySQL 的查询优化器如何解析 LEFT (OUTER) JOINS 为例,以及它们在每次迭代时可以评估的结果执行计划的差异:

      https://dev.mysql.com/doc/refman/8.0/en/outer-join-simplification.html

      从本质上讲,左连接总是不确定的。 IMO - 它们不应在生产代码中使用。

      我更喜欢先用更“老派”的方法编写 JOIN 类型语句,而忽略任何特定的 JOIN 声明。让 RDBMS 查询解析器完成其设计的工作 - 分析您的语句并根据对索引统计和数据模型设计的评估将其转换为最佳执行计划。也就是说,查询解析器/优化器的构建甚至会出错,相信我,我已经看到它发生过很多次了。总的来说,我觉得首先采用这种方法通常会提供足够的基线信息,以便在大多数情况下做出明智的进一步调整决策。

      为了说明 - 使用此线程中的问题查询:

      SELECT `voter`.`ID`, `voter`.`Last_Name`, `voter`.`First_Name`,
             `voter`.`Middle_Name`, `voter`.`Age`, `voter`.`Sex`,
             `voter`.`Party`, `voter`.`Demo`, `voter`.`PV`,
             `household`.`Address`, `household`.`City`, `household`.`Zip`
      FROM (`voter`)
      JOIN `household` ON `voter`.`House_ID`=`household`.`id`
      WHERE `CT` = '5'
      AND `Precnum` = 'CTY3'
      AND  `Last_Name`  LIKE '%Cumbee%'
      AND  `First_Name`  LIKE '%John%'
      
      AND NOT EXISTS (
        SELECT * FROM `elimination`
         WHERE `elimination`.`voter_id` = `voter`.`ID`
      )
      
      ORDER BY `Last_Name` ASC
      LIMIT 30
      

      考虑在没有上述显式 JOIN 和 NOT EXISTS 语句的情况下重写它(假设 WHERE 子句中的非完全限定字段属于选民表):

      SELECT v.`ID`, v.`Last_Name`, v.`First_Name`,
             v.`Middle_Name`, v.`Age`, v.`Sex`,
             v.`Party`, v.`Demo`, v.`PV`,
             h.`Address`, h.`City`, h.`Zip`
      FROM `voter` v, `household` h, `elimination` e
      WHERE v.`House_ID` = h.`id`
      AND v.`ID` != e.`voter_id`
      AND v.`CT` = '5'
      AND v.`Precnum` = 'CTY3'
      AND  v.`Last_Name`  LIKE '%Cumbee%'
      AND  v.`First_Name`  LIKE '%John%'
      ORDER BY v.`Last_Name` ASC
      LIMIT 30;
      

      尝试在语法上以两种方式编写一些未来的 SQL 查询,比较它们的结果,看看你的想法。以我上面建议的方式编写 SQL 还带来了更多与 RDBMS 无关的额外好处。

      干杯!

      【讨论】:

      • 很长,保持简洁!
      • 感谢您的建议 - 非常感谢。多年来,我自己阅读了无数线程来寻找答案——我决定是时候开始回馈社会了。从现在开始,我会确保它保持简洁。
      猜你喜欢
      • 1970-01-01
      • 2012-10-18
      • 2014-12-14
      • 2020-12-16
      • 1970-01-01
      • 2013-03-15
      • 2018-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多