Como ingressar em duas tabelas para obter linhas ausentes na segunda tabela

21

Num sistema de votação simples 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 obter a lista de eleições que um usuário votou, o seguinte JOIN é usado

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

mas como obter a lista de eleições em que um usuário NÃO votou?

Googlebot
fonte

Respostas:

21

Use sua consulta existente para obter o oposto da lista desejada. Essa lista pode ser comparada via NOT IN para obter a lista desejada.

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
fonte
17

Use uma junção 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

O UserId ficará vazio se nenhum voto tiver sido lançado para uma eleição específica, caso contrário, ele será exibido

Se você deseja listar apenas as eleições em que não há votos expressos, faça o seguinte:

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

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

Ou seja, a eleição em que não existe um voto do usuário. O NOT INpredicado 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)
);   
Lennart
fonte
-3
SELECT * 
FROM elections 
WHERE election_id NOT IN (
    SELECT DISTINCT(election_id) from votes
);
Dmitriy Grigoryan
fonte
4
Como a resposta aceita gerou eleições nas quais um eleitor específico não votou, isso realmente não responde à pergunta do OP. E, é claro, é apenas uma pequena alteração de uma das outras respostas, para obter eleições nas quais ninguém votou. Um comentário nesse sentido pode fazer com que isso pareça uma resposta um pouco melhor. Ainda assim, pela foto, muito bom para um macaco! :-)
RDFozz 7/11