2009-08-17 15 views
11

Come si crea un'istruzione SQL che restituisce risultati modificati da una sottoquery o un join o qualcos'altro che si occupa delle informazioni che si sta tentando di restituire?SQL: alternativa "NOT IN" per selezionare le righe in base ai valori di * diverse * righe?

Ad esempio:

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

Qualcuno potrebbe essere erroneamente in più di una squadra:

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

Quindi homer non può decidere per la sua squadra, quindi è su entrambi. Do'h!

Voglio conoscere tutti quelli che sono attivi, the homer team che non è anche nella squadra pin pals. Il meglio che posso fare è questo:

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

Con conseguente:

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

Che, si sa, brillante!

Le prestazioni ne risentono, come la subquery sta per essere eseguito per ciascun risultato della query, che è B alla A alla D. Grande per un paio di righe, piuttosto male per le centinaia di migliaia di filari.

Che cos'è un modo migliore? Per lo più penso che unirsi con se stessi farebbe il trucco, ma non riesco a spiegarmi come farlo.

Ci sono altri modi per fare questo, senza l'utilizzo, NOT IN(SELECT ...)

Inoltre, qual è il nome per questo tipo di problema?

+1

left outer si uniscono a te stesso è ciò che desideri. –

risposta

15

Ti piace questa:

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; 

È anche possibile fare in questo modo:

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

Tra l'altro, questo si chiama "Sinistra anti-Semi Join".

+0

Fantastico! Il tuo primo esempio è sicuramente un miglioramento del mio problema. Ancora in esecuzione un po 'lento (MySQL versione 5.0.37) ma alcuni ... beh, almeno ora sta tornando! Grazie per il termine per spiegare questo (Left Anti-Semi Join) –

2

È possibile LEFT JOIN e assicurarsi che la tabella unita non contenga dati (tutto è nullo).

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

Funziona sicuramente, ma è più o meno efficiente della sua subquery? Sembra che ci sarebbe più un sovraccarico sul join, ma non lo so. – chrissr

+0

Dipende molto dalla struttura della tabella, quali indici sono disponibili, numero di righe nella tabella, ecc., Quante persone ci sono nella squadra A o nella squadra B, ecc. –

Problemi correlati