2009-08-17 6 views
11

Comment faire une instruction SQL qui renvoie des résultats modifiés par une sous-requête, ou une jointure - ou quelque chose d'autre, qui traite des informations que vous essayez de renvoyer?SQL: "NOT IN" alternative pour sélectionner des lignes basées sur des valeurs de * différentes * lignes?

Par exemple:

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

Quelqu'un pourrait à tort être sur plus d'une équipe:

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'); 

Alors homer ne peut pas décider de son équipe, alors il est à la fois. Do'h!

Je veux connaître tous ceux qui sont sur, the homer team qui ne fait pas partie de l'équipe pin pals. Le mieux que je peux faire est la suivante:

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'); 

Entraînant:

+-----------------------+----------------+ 
| 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) 

Ce qui, vous le savez, génial!

La performance va souffrir, car la sous-requête va être exécutée pour chaque résultat de la requête, qui est B à la A à la D. Idéal pour quelques lignes, assez mauvais pour les centaines de milliers de lignes

Quoi de mieux? Je pense surtout qu'une auto-adhésion ferait l'affaire, mais je ne peux pas comprendre comment faire ça.

Y at-il d'autres façons de le faire, sans utiliser, NOT IN(SELECT ...)

Aussi, quel est le nom de ce type de problème?

+1

gauche externe rejoindre à vous-même est ce que vous voulez. –

Répondre

15

Comme ceci:

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; 

Vous pouvez aussi le faire comme ceci:

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 
    ); 

Par ailleurs, on appelle cela une "Gauche Anti-Semi Join".

+0

Brilliant! Votre premier exemple est certainement une amélioration de mon problème. Toujours en cours d'exécution un peu lent (MySQL version 5.0.37) mais quelques uns ... eh bien, au moins maintenant ça revient! Merci pour le terme d'expliquer cela par (Left Anti-Semi Join) –

2

Vous pouvez LEFT JOIN et vous assurer que la table jointe ne contient aucune donnée (tout est nul).

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

Cela fonctionne définitivement, mais est-il plus ou moins efficace que sa sous-requête? On dirait qu'il y aurait plus de frais généraux sur la jointure, mais je ne sais pas. – chrissr

+0

Cela dépend fortement de la structure de la table, des index disponibles, du nombre de lignes dans la table, etc., du nombre de personnes dans l'équipe A ou B, etc. –

Questions connexes