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 EXISTS
predicado:
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 IN
predicado 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)
);