Есть много способов добиться того, что вы просите. Возможно, самый простой способ - использовать чисто ориентированный на множество подход:
select election_id from elections
minus -- except is used instead of minus by some vendors
select election_id from votes where user_id = ?
Из списка выборов мы удаляем те, за которые проголосовал пользователь. Результат может быть объединен с выборами, чтобы получить название выборов. Даже если вы не отметили свой вопрос, есть основания полагать, что вы используете MySQL, и MINUS или EXCEPT там не поддерживаются.
Другой вариант - использовать NOT EXISTS
предикат:
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 = ?
);
Т.е. на выборах, где не существует голосования от пользователя. NOT IN
Предикат может быть использован аналогичным образом. Поскольку могут быть задействованы значения NULL, стоит отметить, что семантика отличается между IN и EXISTS.
Наконец, вы можете использовать внешнее соединение
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;
Если нет строк, соответствующих предикату ON, в результате все столбцы из голосов заменяются на ноль. Поэтому мы можем проверить, является ли какой-либо столбец из голосования нулевым в предложении WHERE. Поскольку оба столбца в голосах могут быть нулевыми, вам нужно быть осторожным.
В идеале вы должны исправить ваши таблицы, чтобы вам не приходилось иметь дело с ошибками, вызванными нулями:
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)
);