Existem várias maneiras de conseguir o que você está pedindo. Talvez a maneira mais direta seja usar uma abordagem puramente orientada a conjuntos:
select election_id from elections
minus -- except is used instead of minus by some vendors
select election_id from votes where user_id = ?
Do conjunto de eleições, removemos aquelas em que o usuário votou. O resultado pode ser associado às eleições para obter o título das eleições. Mesmo que você não tenha marcado sua pergunta, há motivos para acreditar que você está usando o MySQL e o MINUS ou EXCEPT não é suportado lá.
Outra variante é usar o 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 = ?
);
Ou seja, a eleição em que não existe um voto do usuário. O NOT IN
predicado pode ser usado de maneira semelhante. Como pode haver nulos envolvidos, é importante notar que a semântica difere entre IN e EXISTS.
Por fim, você pode usar uma junção 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;
Se não houver linhas que correspondam ao predicado ON, todas as colunas dos votos serão substituídas por nulas no resultado. Podemos, portanto, verificar se alguma coluna de votos é nula na cláusula WHERE. Como as duas colunas em votos podem ser nulas, você precisa ter cuidado.
Idealmente, você deve corrigir suas tabelas para não precisar lidar com as dicas 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)
);