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 â ââââââ©ââââââââââ
5 Réponses :
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'
Une banane est censée être une orange, je suppose. ;)
@stickybit derp duh derp derp. Merci
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 );
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')
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.)
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
.
Il s'agit d'un cas de relationnel-division .
Si (comme c'est le cas typique, et vos exemples de données semblent le soutenir) ...
FOREIGN KEY
imposant l'intégrité référentielle 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.
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 tableuser
? Existe-t-il une contrainteFOREIGN KEY
pour appliquer l'intégrité référentielle? Une contrainteUNIQUE
pour interdire les dupes surproduct (product, fk_user)
? Votre version de Postgres?