Comment joindre deux tables pour obtenir des lignes manquantes dans la deuxième table

21

Dans un système de vote simple comme

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

pour obtenir la liste des élections qu'un utilisateur a voté, le JOIN suivant est utilisé

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

mais comment obtenir la liste des élections qu'un utilisateur n'a PAS voté?

Googlebot
la source

Réponses:

21

Utilisez votre requête existante pour obtenir l'opposé de la liste souhaitée. Cette liste peut ensuite être vérifiée via NOT IN pour obtenir la liste souhaitée.

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
la source
17

Utilisez une jointure externe:

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

L'ID utilisateur sera vide si aucun vote n'a été émis pour une élection particulière, sinon il apparaîtra

Si vous voulez seulement lister les élections où il n'y a pas de votes, vous pouvez le faire comme ceci:

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

Il existe de nombreuses façons de réaliser ce que vous demandez. La façon la plus simple est peut-être d'utiliser une approche purement orientée sur les ensembles:

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

De l'ensemble des élections, nous supprimons celles pour lesquelles l'utilisateur a voté. Le résultat peut être joint aux élections pour obtenir le titre des élections. Même si vous n'avez pas marqué votre question, il y a des raisons de croire que vous utilisez MySQL, et MINUS ou EXCEPT n'y est pas pris en charge.

Une autre variante consiste à utiliser le NOT EXISTSprédicat:

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

C'est-à-dire l'élection où il n'existe pas de vote de l'utilisateur. Le NOT INprédicat peut être utilisé d'une manière similaire. Puisqu'il peut y avoir des valeurs nulles impliquées, il convient de noter que la sémantique diffère entre IN et EXISTS.

Enfin, vous pouvez utiliser une jointure externe

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;

S'il n'y a pas de lignes qui correspondent aux prédicats ON, toutes les colonnes des votes sont remplacées par null dans le résultat. Nous pouvons donc vérifier si une colonne de votes est nulle dans la clause WHERE. Étant donné que les deux colonnes des votes peuvent être nulles, vous devez être prudent.

Idéalement, vous devriez corriger vos tables de sorte que vous n'ayez pas à faire face aux pièges causés par les nulls:

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
la source
-3
SELECT * 
FROM elections 
WHERE election_id NOT IN (
    SELECT DISTINCT(election_id) from votes
);
Dmitriy Grigoryan
la source
4
Comme la réponse acceptée a entraîné des élections où un électeur spécifique n'a pas voté, cela ne répond pas vraiment à la question du PO. Et, bien sûr, ce n'est qu'un petit ajustement de l'une des autres réponses, pour obtenir des élections où personne n'a voté. Un commentaire à cet effet pourrait donner l'impression que la réponse est un peu meilleure. Pourtant, d'après la photo, assez bon pour un singe! :-)
RDFozz