Cómo unir dos tablas para obtener filas que faltan en la segunda tabla

21

En un sistema de votación simple como

CREATE TABLE elections (
election_id int(11) NOT NULL AUTO_INCREMENT,
title varchar(255),

CREATE TABLE votes (
election_id int(11),
user_id int(11),
FOREIGN KEYs

para obtener la lista de elecciones que un usuario ha votado, se utiliza el siguiente JOIN

SELECT * FROM elections
JOIN votes USING(election_id)
WHERE votes.user_id='x'

pero, ¿cómo obtener la lista de elecciones que un usuario NO ha votado?

Googlebot
fuente

Respuestas:

21

Use su consulta existente para obtener el opuesto de la lista que desea. Esa lista puede verificarse mediante NOT IN para obtener la lista deseada.

SELECT * FROM elections WHERE election_id NOT IN (
    SELECT elections.election_id from elections
    JOIN votes USING(election_id)
    WHERE votes.user_id='x'
)
Sparr
fuente
17

Use una unión externa:

select e.election_id, e.title, v.user_id
from Elections e
 LEFT OUTER JOIN votes v ON v.election_id = e.election_id and v.user_id = @userid

El ID de usuario estará vacío si no se han emitido votos para una elección en particular; de lo contrario, aparecerá

Si solo desea enumerar las elecciones donde no hay votos emitidos, puede hacerlo así:

select *
from elections e
where election_id NOT IN 
 (select election_id
  from votes
  where user_id = @userid
 )
druzin
fuente
5

Hay muchas maneras de lograr lo que está pidiendo. Quizás la forma más directa es utilizar un enfoque orientado exclusivamente al conjunto:

select election_id from elections
minus -- except is used instead of minus by some vendors
select election_id from votes where user_id = ?

Del conjunto de elecciones, eliminamos aquellos donde el usuario ha votado. El resultado se puede unir a las elecciones para obtener el título de las elecciones. Aunque no haya etiquetado su pregunta, hay razones para creer que está utilizando MySQL, y MINUS o EXCEPT no son compatibles allí.

Otra variante es usar el NOT EXISTSpredicado:

select election_id, title 
from elections e
where not exists (
    select 1 
    from votes v
    where e.election_id = v.election_id
      and v.user_id = ?
);

Es decir, la elección donde no existe un voto del usuario. El NOT INpredicado se puede usar de manera similar. Como puede haber nulos involucrados, vale la pena señalar que la semántica difiere entre IN y EXISTS.

Finalmente, puedes usar una combinación externa

select election_id, title 
from elections e
left join votes v
    on e.election_id = v.election_id
   and v.user_id = ?
where v.user_id is null;

Si no hay filas que coincidan con el predicado ON, todas las columnas de los votos se reemplazan por nulas en el resultado. Por lo tanto, podemos verificar si alguna columna de votos es nula en la cláusula WHERE. Como ambas columnas en los votos pueden ser nulas, debe tener cuidado.

Idealmente, debe arreglar sus tablas para que no tenga que lidiar con las trampas causadas por nulos:

CREATE TABLE elections 
( election_id int NOT NULL AUTO_INCREMENT PRIMARY KEY
, title varchar(255) not null );

CREATE TABLE votes 
( election_id int not null
, user_id int not null
,     constraint pk_votes primary key (election_id, user_id)
,     constraint fk_elections foreign key (election_id)
                              references elections (election_id)
);   
Lennart
fuente
-3
SELECT * 
FROM elections 
WHERE election_id NOT IN (
    SELECT DISTINCT(election_id) from votes
);
Dmitriy Grigoryan
fuente
44
Como la respuesta aceptada eliminó las elecciones donde un votante específico no votó, esto realmente no responde a la pregunta del OP. Y, por supuesto, es solo un pequeño ajuste de una de las otras respuestas, para obtener elecciones donde nadie votó. Un comentario al respecto podría hacer que esto parezca una respuesta un poco mejor. Aún así, de la foto, ¡bastante bueno para un mono! :-)
RDFozz