2010-02-15 1 views
5

Je suis plutôt une débutante et j'ai deux tables: "product" et "product attributes".Sélection par rapport à des sous-ensembles d'une liste dans MySQL

Voici quelques données imaginaires (la substance réelle implique plusieurs tables)

Produits Tableau:

product_id | product_name     
10   | aaa       
11   | bbb 
12   | ccc 

produit Attributs Tableau:

attribute_id | product_id 
     21  | 10   
     23  | 10   
     24  | 10   
     21  | 11   
     24  | 11   
     21  | 12   
     25  | 12   

où chaque produit a plus d'un attribut possible . J'ai une liste d'identifiants d'attribut comme (21,10,25) et je dois sélectionner tous les produits dont les attributs sont un sous-ensemble de cette liste.

Est-il possible de le faire en une seule requête?

Quand je filtre pour (21,24) de sortie souhaitée est de retourner le produit seulement 11 (bbb)

Quand je filtre pour (21,23,24) de sortie souhaitée est de retourner les produits 10 et 11.

Lorsque je filtre pour (21) la sortie désirée est de n'en renvoyer aucune (car tous les produits ont au moins un autre attribut).

Répondre

5

Si vous prétendez que votre filtre est dans une table:

select * 
from product p 
where not exists (
    select 1 
    from attributes a 
    where a.product_id = p.product_id 
    and not exists(
     select 1 
     from filter f 
     where f.id_attribute = a.id_attribute)) 

Si elle était dans une requête construite:

select * 
from product p 
where not exists (
    select 1 
    from attributes a 
    where a.product_id = p.product_id 
    and attribute_id not in (<list>)) 

Ceci est hors de ma tête, donc peut avoir des fautes de frappe.

+1

oui, le second fonctionne, merci –

+0

Cela fonctionne merci :) – Ashu

1

En supposant que votre table de produit est appelé produit et la colonne ID dans ce tableau est simplement appelé Id:

SELECT * from Product p where p.Id IN 
    (Select id_product from ProductAttributes where id_attribute in (21, 23, 24)) 
+0

oui mais cela sélectionnerait aussi des produits ayant un attribut dans la liste et l'autre pas dans la liste. Je l'ai déjà fait comme ça.J'ai besoin d'avoir tous les attributs dans la liste (comme je l'ai dit), donc chaque ligne possible de la table attributaire avec cette ID_produit –

1
select 
    P.id, 
    P.name, 
    count(P.id) as matched_attr_count, 
    count(PA.a_id) as total_attr_count 
from 
    product_attributes PA 
    left join product P on P.id = PA.p_id and PA.a_id in (21,23,24) 
group by 
    PA.p_id 
having 
    matched_attr_count = total_attr_count; 
+0

cette solution semble également fonctionner, sauf qu'elle retourne plus de lignes par produit, de sorte que P. profite à un groupe. id_product dedans edit: non, ça ne marche pas .. même problème, il ne filtre pas tous les attributs d'un produit, il suffit que l'un d'entre eux soit dans la liste –

+0

oui mais il y a aussi l'autre problème qu'il ne filtre pas tous les attributs –

+0

Je vous ai mal compris d'abord. On dirait fixe maintenant? – Qwerty

-1

me laisser publier des données simples imaginaires (les choses réelles implique plusieurs tables)

Table produits

product_id | product_name     
10   | aaa       
11  | bbb 

Table product_attribute

attribute_id | product_id <br> 
21  | 10   
23  | 10   
24  | 10   
21  | 11   
24  | 11 

je veux que:

  • quand je filtre pour (21,24) à retourner seul produit 11 (bbb)
  • quand je filtre pour (21,23,24) à retourner à la fois produits
  • lorsque je filtre pour (21) seulement pour être renvoyé aucun (parce que pas pro conduit a seulement cet attribut)
+0

ce truc est en prestashop, si vous avez besoin d'un module pour filtrer par attributs , mais des tables et des relations plus complexes –

+1

Je suggère que vous modifiez la question et ajoutez ceci à la question. –

+1

Ce type d'information devrait être inclus dans votre question originale. Il est possible de modifier votre question si vous souhaitez inclure plus d'informations ultérieurement. J'ai modifié votre question pour vous. –

1

Cela devrait retourner uniquement les id où tous les attributs pour chaque ID sont complètement intégrés dans la liste:

select attribute_match.id_product from 
(select id_product, count(*) c from attributes 
    where id_attribute in (21, 10, 25) 
    group by id_product) attribute_match, 
(select id_product, count(*) c_count from attributes 
    group by id_product) attribute_total 
where attribute_match.id_product = attribute_total.id_product 
     and attribute_match.c = attribute_total.c 
+0

cela ne fonctionne pas dans le cas (21,23,24) (sélectionne seulement 1 produit) –

+0

Je pense que la requête révisée devrait fonctionner - le premier sélection interne devrait générer 10,3 11,2 et le second sélection interne devrait générer 10 , 3 11,2 et donc la sélection externe devrait correspondre à la fois 10 et 11. – user273224

+0

oui, maintenant ça marche, un moyen plus facile a été posté, mais vous aurez encore un vote –

1

Jusqu'à ce que MySQL prend en charge la combinaison EXCEPT de requête,

SELECT product_id 
    FROM attributes 
    WHERE product_id NOT IN (
     SELECT product_id 
     FROM attributes 
     WHERE attribute_id NOT IN (21, 23, 24) 
    ) 
    GROUP BY product_id 
UNION 
SELECT id 
    FROM products AS p 
    LEFT JOIN attributes AS a 
    ON p.id = a.product_id 
    WHERE a.product_id IS NULL 

Si vous souhaitez avoir les produits avec tous les attributs donnés, ajoutez une clause HAVING COUNT(*)=n à la première requête externe, où « n » est la longueur de la liste d'attributs.

+0

même, ne fonctionne pas dans le cas (21,23,24) selon mon exemple –

+0

@Bogdan: code original était pour révision de la question 3. Mise à jour pour correspondre à la révision en cours (7). – outis

+0

Je n'ai pas modifié la question, je l'ai juste mieux expliqué avec des exemples. Merci pour l'effort de toute façon, vous avez un vote de moi –

0

Sur la base de vos gars insight, j'optimisé encore plus loin et utilisé seulement 1 déclaration COUNT comme ceci:

SELECT * ,COUNT(p.product_id) AS c FROM product_attribute pa 
LEFT JOIN products p ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter_list) 
GROUP BY pa.product_id 
HAVING c=0 

-t-il? :)

Editer:
Ce code ne retourne pas le nom du produit ou d'autres champs qu'il pourrait avoir. C'est le bon:

SELECT * ,COUNT(pa.product_id) AS c FROM products p 
LEFT JOIN product_attribute pa ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter) 
GROUP BY p.product_id 
HAVING c=0 
Questions connexes