Присоединение к MySQL там, где его не существует

80

У меня есть запрос MySQL, который объединяет две таблицы

  • Избиратели
  • Домохозяйства

Они присоединяются к voters.household_idи household.id.

Теперь мне нужно изменить его, где таблица избирателей соединена с третьей таблицей, называемой исключением, вместе с voter.idи elimination.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 
gsueagle2008
источник

Ответы:

191

Я бы, вероятно, использовал 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

Будет ли это более или менее эффективным, чем использование подзапроса, зависит от оптимизации, индексов, от того, возможно ли иметь более одного исключения для каждого избирателя и т. Д.

NickZoic
источник
3
+1 намного быстрее при высокой нагрузке, чем подзапросы + если U может выполнять JOINs вместо подзапросов - просто выполняйте JOINs, они намного проще для анализатора. Другой полезный пример: U может захотеть получить результат, если в правой таблице есть несколько строк или их нет: SELECT V.* FROM voter V LEFT JOIN elimination E ON V.id = E.voter_id OR E.voter_id IS NULLнапример: если U не хочет хранить все записи в правой таблице для каждой строки слева.
Arthur Kushman
1
Как бы вы изменили этот запрос, чтобы найти строки, которых нет E, когда они E.voter_idмогут быть NULLв наборе данных, из которого мы находимся JOIN?
Дэнни Беккет
Вам необходимо связать таблицы вместе с каким-либо общим столбцом или связанным значением. Но я думаю, что это может сработать (непроверено):SELECT V.*, COUNT(E.*) AS `countE` FROM voter V LEFT JOIN elimination E ON V.id = E.voter_id WHERE countE = 0;
ericek111
7

Я бы использовал «там, где не существует» - именно так, как вы предлагаете в своем заголовке:

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.

Ян Клелланд
источник
Спасибо за это - для меня это было определенно быстрее.
spidie
6

Это можно сделать тремя способами.

  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
    )
    
Думинду Мадушанка
источник