SQL-запрос для поиска записи с идентификатором не в другой таблице

123

У меня есть две таблицы с привязкой первичного ключа в базе данных, и я хочу найти непересекающийся набор между ними. Например,

  • Table1имеет columns ( ID, Name) и образцы данных:(1 ,John), (2, Peter), (3, Mary)
  • Table2имеет columns ( ID, Address) и образцы данных:(1, address2), (2, address2)

Итак, как мне создать SQL-запрос, чтобы я мог получить строку с идентификатором table1, которого нет table2. В этом случае (3, Mary)следует вернуть?

Ps. ID - это первичный ключ для этих двух таблиц.

Заранее спасибо.

johnklee
источник
3
В качестве совета для будущих вопросов: всегда определяйте, какую систему базы данных (и какую версию этой базы данных) вы используете. SQL - это просто язык структурированных запросов, используемый в большинстве систем баз данных, который на самом деле не очень помогает ... часто базы данных имеют расширения и функции, выходящие за рамки стандарта ANSI / ISO SQL, которые упрощают решение проблемы, но для этого вы нужно сообщить нам, какую базу данных вы используете
marc_s
5
@marc_s: Что, если они ищут решение, не зависящее от языка, потому что им необходимо поддерживать несколько базовых систем баз данных, или реализация базы данных абстрагирована?
dwanderson 02
Привет @marc_s, в этом случае я использую PostgreSQL. Спасибо за напоминание.
johnklee

Ответы:

213

Попробуй это

SELECT ID, Name 
FROM   Table1 
WHERE  ID NOT IN (SELECT ID FROM Table2)
Принц Jea
источник
8
@PrinceJea на самом деле это зависит от обстоятельств. См. Здесь для разъяснения
Джон Ву
Когда у меня есть 20 данных, это работает, но когда у меня есть 20000 данных, это не работает, теперь я запутался.
Фрэнк
1
Нет представления, почему, но это не работает. У меня около 10000 строк в таблице. В моем случае решение @JohnWoo работало нормально.
Мунам Юсуф
4
Это не сработает, если в «Not In» слишком много значений, так как этот метод имеет ограниченное количество значений cf: dba-oracle.com/t_maximum_number_of_sql_in_list_values.htm
G.Busato
2
Мне пришлось сделать это так: выберите i из таблицы 1, ГДЕ Я НЕ ВХОДИТ (ВЫБЕРИТЕ i из таблицы 2, где i не равно нулю ), и я не равен нулю
jaksco
93

использование LEFT JOIN

SELECT  a.*
FROM    table1 a
            LEFT JOIN table2 b
                on a.ID = b.ID
WHERE   b.id IS NULL
Джон Ву
источник
Я думаю, что это более быстрый подход для очень большой базы данных
Алекс Джолиг
12

Есть в основном 3 подхода к тому , что: not exists, not inи left join / is null.

LEFT JOIN с IS NULL

SELECT  l.*
FROM    t_left l
LEFT JOIN
        t_right r
ON      r.value = l.value
WHERE   r.value IS NULL

НЕ В

SELECT  l.*
FROM    t_left l
WHERE   l.value NOT IN
        (
        SELECT  value
        FROM    t_right r
        )

НЕ СУЩЕСТВУЕТ

SELECT  l.*
FROM    t_left l
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    t_right r
        WHERE   r.value = l.value
        )

Какой лучше? Ответ на этот вопрос было бы лучше разделить на основных конкретных поставщиков СУБД. Вообще говоря, следует избегать использования, select ... where ... in (select...)когда величина количества записей в подзапросе неизвестна. Некоторые поставщики могут ограничивать размер. Oracle, например, имеет ограничение в 1000 . Лучше всего попробовать все три и показать план выполнения.

Конкретно форма PostgreSQL, план выполнения NOT EXISTSи LEFT JOIN / IS NULLтакой же. Я лично предпочитаю этот NOT EXISTSвариант, потому что он лучше показывает намерение. В конце концов семантический, что вы хотите , чтобы найти записи в том , что его рк не существует в B .

Старый, но все же золотой, но характерный для PostgreSQL: https://explainextended.com/2009/09/16/not-in-vs-not-exists-vs-left-join-is-null-postgresql/

Л. Холанда
источник
10

Быстрая альтернатива

Я провел несколько тестов (на postgres 9.5), используя две таблицы по ~ 2 млн строк в каждой. Этот запрос ниже работал как минимум в 5 * лучше, чем другие предложенные запросы:

-- Count
SELECT count(*) FROM (
    (SELECT id FROM table1) EXCEPT (SELECT id FROM table2)
) t1_not_in_t2;

-- Get full row
SELECT table1.* FROM (
    (SELECT id FROM table1) EXCEPT (SELECT id FROM table2)
) t1_not_in_t2 JOIN table1 ON t1_not_in_t2.id=table1.id;
polvoazul
источник
1
Это было не быстрее, чем решение @Jhon Woo. Я использую Postgres 9.6, а время выполнения решения Jhon составляет около 60 мс. Пока я вполне себе это решение через 120 сек и никакого результата.
froy001
5

Принимая во внимание моменты, сделанные в комментарии / ссылке @John Woo выше, я обычно поступаю следующим образом:

SELECT t1.ID, t1.Name 
FROM   Table1 t1
WHERE  NOT EXISTS (
    SELECT TOP 1 NULL
    FROM Table2 t2
    WHERE t1.ID = t2.ID
)
CaseyR
источник
2
SELECT COUNT(ID) FROM tblA a
WHERE a.ID NOT IN (SELECT b.ID FROM tblB b)    --For count


SELECT ID FROM tblA a
WHERE a.ID NOT IN (SELECT b.ID FROM tblB b)    --For results
JoshYates1980
источник