89

我有一个连接两个表的 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 
4

4 回答 4

223

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

所以,像:

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

这比使用子查询效率高还是低取决于优化、索引、每个选民是否可能有多个淘汰等。

于 2009-04-15T05:20:18.900 回答
10

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

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 快得多

于 2009-05-08T16:49:01.897 回答
9

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

  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
    )
    
于 2017-04-07T14:54:32.603 回答
-1

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

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

LEFT JOINS 就其本质而言始终是非确定性的。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 无关的额外好处。

干杯!

于 2021-06-30T23:58:11.577 回答