2009-08-17 20 views
11

¿Cómo se hace una declaración de SQL que arroja resultados modificados por una subconsulta, o una combinación, o alguna otra cosa, que trata de la información que intentas devolver?SQL: alternativa "NO EN" para seleccionar filas basadas en valores de * filas * diferentes?

Por ejemplo:

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

Alguien podría ser incorrecta en más de un equipo:

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

Así homer no puede decidir sobre su equipo, por lo que hay en ambos. Do'h!

Quiero saber a todos los que están en, the homer team que no está también en el equipo pin pals. Lo mejor que puedo hacer es esto:

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

El resultado es:

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

Lo que, ya sabes, brillante!

El rendimiento se verá afectada, ya que la subconsulta se va a ejecutar para cada resultado de la consulta, que es B a la A a la D. Grande para un par de filas, Bastante malo para los cientos de miles de filas

¿Cuál es la mejor manera? En su mayoría, estoy pensando que unirse me haría el truco, pero no puedo entender cómo hacerlo.

¿Hay otras maneras de hacer esto, sin usar, NOT IN(SELECT ...)

Además, ¿cuál es el nombre para este tipo de problema?

+1

left outer join volver a usted es lo que quiere. –

Respuesta

15

De esta manera:

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; 

También puede hacerlo de esta manera:

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

Por cierto, esto se llama una "izquierda anti-Semi Join".

+0

¡Brillante! Tu primer ejemplo ciertamente es una mejora de mi problema. Todavía funciona un poco lento (MySQL versión 5.0.37) pero algunos ... bueno, ¡al menos ahora está volviendo! Gracias por el término para explicar esto por (Left Anti-Semi Join) –

2

Puede LEFT JOIN y asegúrese de que la tabla unida no tenga datos (todo es nulo).

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

Esto definitivamente funciona, pero ¿es más o menos eficiente que su subconsulta? Parece que habría más gastos generales en la unión, pero no sé. – chrissr

+0

Depende en gran medida de la estructura de la tabla, qué índices están disponibles, el número de filas en la tabla, etc., cuántas personas están en el equipo A o equipo B, etc. –

Cuestiones relacionadas