1
votes

Conditions multiples dans INNER JOIN

J'ai deux tables user et product qui ont une relation un-à-plusieurs (un utilisateur peut avoir plusieurs produits s).

Je veux créer une requête pour obtenir tous les utilisateurs qui ont à la fois une orange et une banane. Dans mon exemple ci-dessous, ce serait john et leeroy.

Comment puis-je formuler ma requête pour faire cela?

Avec une seule condition, j'irais comme:

╔═════════╦═════════╗
â•‘ product â•‘ fk_user â•‘
╠═════════╬═════════╣
â•‘ orange  â•‘ 1       â•‘
╠═════════╬═════════╣
â•‘ orange  â•‘ 2       â•‘
╠═════════╬═════════╣
â•‘ banana  â•‘ 2       â•‘
╠═════════╬═════════╣
â•‘ banana  â•‘ 3       â•‘
╠═════════╬═════════╣
â•‘ orange  â•‘ 3       â•‘
╠═════════╬═════════╣
â•‘ banana  â•‘ 4       â•‘
╚═════════╩═════════╝

La table user

╔════╦═════════╗
â•‘ id â•‘ name    â•‘
╠════╬═════════╣
â•‘ 1  â•‘ michael â•‘
╠════╬═════════╣
â•‘ 2  â•‘ john    â•‘
╠════╬═════════╣
â•‘ 3  â•‘ leeroy  â•‘
╠════╬═════════╣
â•‘ 4  â•‘ tony    â•‘
╚════╩═════════╝


3 commentaires

Une jointure peut ne pas suffire pour cela.


Vous n'avez pas besoin d'une jointure. Voir ma réponse à ce sujet. Ce n'est pas une table de produits d'ailleurs; car un enregistrement dans la table ne représente pas uniquement un produit. Vous devriez idéalement avoir une table product avec une ligne par produit et une table user_product liant le produit et l'utilisateur.


Avez-vous simplement besoin de user_id ou de plusieurs colonnes de la table user ? Existe-t-il une contrainte FOREIGN KEY pour appliquer l'intégrité référentielle? Une contrainte UNIQUE pour interdire les dupes sur product (product, fk_user) ? Votre version de Postgres?


5 Réponses :


3
votes

Vous pouvez utiliser deux jointures:

SELECT u.* 
FROM user u
INNER JOIN product p1
ON p1.fk_user=u.id
AND p1.product='banana'
INNER JOIN product p2
ON p2.fk_user=u.id
AND p2.product='orange'


2 commentaires

Une banane est censée être une orange, je suppose. ;)


@stickybit derp duh derp derp. Merci



3
votes

Groupez par utilisateur et utilisez HAVING pour vérifier les produits de l'utilisateur.

select *
from user
where id in
(
  select fk_user 
  from product 
  where product in ('orange', 'banana') 
  group by fk_user
  having count(distinct product) = 2 -- two different products: orange and banana
);

Edit: Je dois ajouter qu'il existe plusieurs façons d'écrire une telle sous-requête. Une clause WHERE pourrait accélérer cela, et avec une telle clause, vous pourriez simplement compter les produits distincts trouvés:

select *
from user
where id in
(
  select fk_user
  from product
  group by fk_user
  having count(case when product = 'orange' then 1 end) > 0
     and count(case when product = 'banana' then 1 end) > 0
);


0 commentaires

3
votes

La déclaration la plus simple (IMHO) serait d'utiliser deux clauses IN :

select *
from user
where id in
(
  select fk_user
  from product
  WHERE product = 'orange')
and id in
(
  select fk_user
  from product
  WHERE product = 'banana')


2 commentaires

Canonique n'est peut-être pas le bon mot - mon intention est de dire "la manière déclarative la plus simple". C'est le SQL le plus proche qui exprime l ' intention de la requête ( EXISTS est un proche IMHO)


C'est suffisant. ( EXISTS gagnerait cette compétition là où je suis - et cela peut être plus rapide sans jamais être plus lent.)



1
votes

Si vous voulez juste l'ID utilisateur et non le nom, vous pouvez utiliser l'agrégation:

SELECT p.fk_user
FROM product p
WHERE p.product in ('banana', 'orange')
GROUP BY p.fk_user
HAVING COUNT(*) FILTER (WHERE p.product = 'banana') > 0 AND
       COUNT(*) FILTER (WHERE p.product = 'orange') > 0;

Si vous avez également besoin de colonnes supplémentaires provenant de user , je le ferais aller avec la version IN suggérée par DStanley, même si j'utiliserais EXISTS plutôt que IN.


0 commentaires

0
votes

Il s'agit d'un cas de .
Si (comme c'est le cas typique, et vos exemples de données semblent le soutenir) ...

  • tout ce dont vous avez besoin est l'ID utilisateur
  • il existe une contrainte FOREIGN KEY imposant l'intégrité référentielle
  • et une contrainte UNIQUE sur product (product, fk_user) - (implicitement fournir l'index parfait pour ma requête)

.. alors c'est aussi rapide que possible:

SELECT fk_user
FROM   product b
JOIN   product o USING (fk_user)
WHERE  b.product = 'banana'
AND    o.product = 'orange';

Nous avons rassemblé un arsenal de techniques de base ici:

Le meilleur choix dépend des spécifications manquantes - et des préférences personnelles dans une certaine mesure.

À part: user code > est un mot réservé, ne l'utilisez pas comme nom de table.


0 commentaires