2009-08-17 3 views
11

Как вы делаете инструкцию SQL, которая возвращает результаты, измененные подзапросом, или соединение - или что-то еще, которое касается информации, которую вы пытаетесь вернуть?SQL: «NOT IN» альтернатива для выбора строк на основе значений * разных * строк?

Например:

CREATE TABLE bowlers (
bowling_id int4 not null primary key auto_increment, 
name text, 
team text 
); 

Кто-то может неправильно быть более чем одной команды:

INSERT INTO `bowlers` (`name`, `team`) VALUES 
('homer', 'pin pals'), 
('moe', 'pin pals'), 
('carl', 'pin pals'), 
('lenny', 'pin pals'), 
('homer', 'The homer team'), 
('bart', 'The homer team'), 
('maggie', 'The homer team'), 
('lisa', 'The homer team'), 
('marge', 'The homer team'), 
('that weird french guy', 'The homer team'); 

Так homer не может принять решение о своей команде, так что он на обоих. Do'h!

Я хочу знать всех, кто включен, the homer team, который также не входит в команду pin pals. Лучшее, что я могу сделать это:

SELECT a.name, a.team 
    FROM bowlers a where a.team = 'The homer team' 
    AND a.name 
    NOT IN (SELECT b.name FROM bowlers b WHERE b.team = 'pin pals'); 

Результирующее в:

+-----------------------+----------------+ 
| name     | team   | 
+-----------------------+----------------+ 
| bart     | The homer team | 
| maggie    | The homer team | 
| lisa     | The homer team | 
| marge     | The homer team | 
| that weird french guy | The homer team | 
+-----------------------+----------------+ 
5 rows in set (0.00 sec) 

Что, вы знаете, гениально!

Производительность будет страдать, как подзапрос будет выполняться для каждого результата запроса, который является B к A до D. Великого в течение нескольких строк, довольно плохо для сот тысяч строк.

Что лучше? Я в основном думаю, что самообслуживание будет делать трюк, но я не могу окунуться в голову, как это сделать.

Есть ли другие способы сделать это, не используя, NOT IN(SELECT ...)

Кроме того, что это имя для такого рода проблемы?

+1

левый внешний присоединиться к себе, что вы хотите. –

ответ

15

Как это:

SELECT a.name, a.team 
FROM bowlers a 
LEFT OUTER JOIN bowlers b ON a.name = b.name AND b.team = 'pin pals' 
WHERE a.team = 'The homer team' 
AND b.name IS NULL; 

Вы также можете сделать это следующим образом:

SELECT a.name, a.team 
FROM bowlers a 
WHERE a.team = 'The homer team' 
AND NOT EXISTS (SELECT * FROM bowlers b 
    WHERE b.team = 'pin pals' 
    AND a.name = b.name 
    ); 

Кстати, это называется "Left Anti-Semi Join".

+0

Brilliant! Ваш первый пример, безусловно, является улучшением моей проблемы. Все еще работает немного медленно (MySQL версии 5.0.37), но несколько ... ну, по крайней мере, теперь он возвращается! Спасибо за этот термин, чтобы объяснить это (Left Anti-Semi Join) – 2009-08-17 18:36:04

2

Вы можете указать LEFT JOIN и убедиться, что в объединенной таблице нет данных (все равно null).

SELECT a.name, a.team 
    FROM bowlers a 
    LEFT JOIN bowlers b 
     ON b.name = a.name AND b.team = 'pin pals' 
    WHERE a.team = 'The homer team' 
    AND a.name 
    -- the join has to fail for this to be null 
    AND b.bowling_id IS NULL 
+0

Это определенно работает, но является ли оно более или менее эффективным, чем его подзапрос? Похоже, в этом будет больше накладных расходов, но я не знаю. – chrissr

+0

Он сильно зависит от структуры таблицы, какие индексы доступны, количество строк в таблице и т. Д., Сколько людей в команде A или команде B и т. Д. –