Як вибрати рядки без відповідного запису в іншій таблиці?


323

Я виконую деякі роботи з обслуговування додатку бази даних, і я виявив, що, радість радостей, навіть якщо значення з однієї таблиці використовуються в стилі іноземних ключів, немає ніяких обмежень для зовнішніх ключів у таблицях.

Я намагаюся додати обмеження FK в ці стовпці, але я знаходжу це, оскільки в таблицях вже є ціле навантаження поганих даних за попередніми помилками, які були наївно виправлені, мені потрібно знайти рядки, які не відповідність іншій таблиці та видаліть їх.

У Інтернеті я знайшов кілька прикладів такого запиту, але всі вони, мабуть, надають приклади, а не пояснення, і я не розумію, чому вони працюють.

Чи може хтось пояснити мені, як побудувати запит, який повертає всі рядки без збігів у іншій таблиці, і що це робить, щоб я міг сам робити ці запити, а не прибігав до SO для кожної таблиці в цьому безладі, який має немає обмежень ФК?

Відповіді:


613

Ось простий запит:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

Ключові моменти:

  1. LEFT JOINвикористовується; це поверне ВСІ рядки з Table1, незалежно від того, існує чи не відповідає рядку Table2.

  2. WHERE t2.ID IS NULLположення; це обмежить результати, що повертаються, лише до тих рядків, з яких повернувся ідентифікатор Table2недійсний - іншими словами, для цього конкретного ідентифікатора НЕ записується . буде повернено як NULL для всіх записів, з яких ідентифікатор не збігається .Table2Table1Table2.IDTable1Table2


4
Помилка, якщо ідентифікатор NULL
Майкл

168
@Michael - Якщо наявність NULLідентифікатора дійсна у вашій схемі, у вас можуть виникнути більші проблеми, чи не згодні ви? :)
rinogo

1
це буде працювати, навіть якщо table1 має більше записів, ніж table2? якщо таблиця1 має 100 записів, а table2 має 200 записів (100, які відповідають / приєднаються, і 100, які не відповідають / приєднаються), чи отримаємо всі 200 записів?
Хуан Велес

1
Мені часто подобається обертати ліву приєднання як перегляд запиту / вбудованого тексту, щоб уникнути взаємозв'язку між пунктом WHERE і лівим приєднанням.
Ендрю Вулф

1
@Jas Ключовий пункт 1 відповіді, ВСІ рядки з першої таблиці, навіть ті, що не відповідають t1.ID = t2.ID умові лівого з’єднання. Якщо змінити перший рядок SELECT t1.ID, t2.IDі видалити рядок WHERE, ви отримаєте краще уявлення про те, як це працює.
Пітер Лабош

97

Я б використовував EXISTSвираження, оскільки це більш потужне, ви можете, тобто точніше вибирати рядки, до яких хотіли б приєднатися, якщо LEFT JOINвам доведеться взяти все, що є в об'єднаній таблиці. Його ефективність, ймовірно, така ж, як і у випадку LEFT JOINз нульовим тестом.

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

Щось таке просте легко обробляється оптимізатором запитів для найкращого виконання.
Ендрю Вулф

2
Так, головна перевага EXISTS- її мінливість.
Ондрей Божек

1
Просто, елегантно і це вирішило мою проблему! Хороший!
MikeMighty

2
Фактично знизив швидкість одного запиту у мене з 7 сек до 200 мс ... (порівняно з WHERE t2.id IS NULL) Дякую.
Моті Корець

4
@MotiKorets ви маєте на увазі збільшення швидкості :)
Ondrej Bozek

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

У таблиці 1 є стовпець, до якого потрібно додати обмеження іноземного ключа, але значення в foreign_key_id_columnне всі збігаються з idтаблицею 2.

  1. Початковий вибір перераховує ids з таблиці1. Це будуть рядки, які ми хочемо видалити.
  2. NOT INПункт в заяві де обмежує запит тільки до рядкам , де значення в foreign_key_id_columnне перебуває у списку таблиці 2 idс.
  3. SELECTЗаява в дужках буде отримати список всіх idз , які знаходяться в таблиці 2.

@ zb226: Ваше посилання на дотик пов'язане з обмеженнями в INпункті зі списком буквальних значень. Це не стосується використання INпункту з результатом підзапиту. Ця прийнята відповідь на це питання фактично вирішує проблему, використовуючи підзапит. (Великий список буквальних значень є проблематичним, оскільки він створює величезний SQL-вираз. Підзапрос працює чудово, оскільки, навіть якщо результат, який ви
отримуєте,

@KannanGoundan Ви абсолютно праві. Вилучення недосконалого коментаря.
zb226

8

Де T2таблиця, до якої ви додаєте обмеження:

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

І видаліть результати.


4

Нехай у нас є наступні 2 таблиці (зарплата та працівник) введіть тут опис зображення

Тепер я хочу тих записів із таблиці працівників, які не мають зарплати. Це можна зробити трьома способами:

  1. Використання внутрішнього приєднання
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

введіть тут опис зображення

  1. Використання лівого зовнішнього з'єднання
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

введіть тут опис зображення

  1. Використання повного приєднання
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

введіть тут опис зображення


2

Із подібного запитання тут MySQL Inner Join Query, щоб отримати записи, відсутні в іншій таблиці, я почав це працювати

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltableтам, де у вас відсутні записи, bigtableтам, де у вас є всі записи. Запит перераховує всі записи, які не існують у, smalltableале існують на bigtable. Ви можете замінити idбудь-якими іншими критеріями відповідності.


0

Ви можете обрати перегляд, як показано нижче:

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

а потім працюйте над поданням для вибору або оновлення:

select * from AuthorizedUserProjectView where projectid = 49

який дає результат, як показано на малюнку нижче, тобто для стовпця, що не відповідає, заповнено нуль.

[Result of select on the view][1]

0

Я не знаю, який з них оптимізований (порівняно з @AdaTheDev), але цей, здається, є швидшим, коли я використовую (як мінімум для мене)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

Якщо ви хочете отримати будь-який інший атрибут, ви можете використовувати:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

Можна зробити щось подібне

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

Як вибрати рядки без відповідного запису в обох таблицях?

    виберіть * з [dbo]. [EmppDetails] e
     право приєднатися [Співробітник]. [Стать] d на e.Gid = d.Gid
    де e.Gid - Null

    союз 
    виберіть * з [dbo]. [EmppDetails] e
     зліва приєднатися [Співробітник]. [Стать] d на e.Gid = d.Gid
    де d.Gid - Null

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.