2009-04-20 9 views
5

Disons que j'ai un Product, Category, et Product_To_Category tableau. Un produit peut être dans plusieurs catégories.SQL pour joindre une table à une autre table plusieurs fois? (Mappage des produits aux catégories)

 
    Product      Category  Product_to_category 
    ID | NAME    ID | Name  Prod_id | Cat_id 
    =====================  ============ =================== 
     1| Rose     1| Flowers   1| 1 
     2| Chocolate Bar   2| Food    2| 2 
     3| Chocolate Flower       3| 1 
                 3| 2 

Je voudrais une requête SQL qui me donne un résultat tel que

 
    ProductName  | Category_1 | Category_2 | Category_3 
    ======================================================= 
    Rose    | Flowers |   | 
    Chocolate Flower | Flowers | Food  | 

etc.

La meilleure façon que je suis en mesure d'obtenir est d'union un groupe de requêtes ensemble; une requête pour chaque nombre attendu de catégories pour un produit donné.

select p.name, cat1.name, cat2.name 
from 
    product p, 
    (select * from category c, producttocategory pc where pc.category_id = c.id) cat1, 
    (select * from category c, producttocategory pc where pc.category_id = c.id) cat2 
where p.id = cat1.id 
    and p.id = cat2.id 
    and cat1.id != cat2.id 
union all 
select p.name, cat1.name, null 
from 
    product p, 
    (select * from category c, producttocategory pc where pc.category_id = c.id) cat1 
where p.id = cat1.id 
    and not exists (select 1 from producttocategory pc where pc.product_id = p.id and pc.category_id != cat1.id) 

Il y a plusieurs problèmes avec ceci.

  • D'abord, je dois répéter cette union pour chaque catégorie attendue; Si un produit peut être dans 8 catégories, j'ai besoin de 8 requêtes. Deuxièmement, les catégories ne sont pas uniformément placées dans les mêmes colonnes. Par exemple, un produit peut parfois avoir «Nourriture, Fleurs» et une autre fois «Fleurs, Nourriture».

Quelqu'un sait-il d'une meilleure façon de le faire? Aussi, cette technique a-t-elle un nom technique?

Répondre

8

Je ne sais pas ce que vous utilisez SGBDR, mais MySQL vous pouvez utiliser GROUP_CONCAT:

SELECT 
    p.name, 
    GROUP_CONCAT(c.name SEPARATOR ', ') AS categories 
FROM 
    product p 
    JOIN product_to_category pc ON p.id = pc.product_id 
    JOIN category c ON c.id = pc.category_id 
GROUP BY 
    p.name 
ORDER BY 
    p.name, 
    c.name 
+2

Je pense que vous devez également ajouter GROUP BY, non? – meleyal

+0

Absolument! J'ai oublié ça, merci! – Seb

+0

Sans le GROUP BY cette requête ne retournera qu'une ligne, de la même manière qu'une requête COUNT (*) ne retournera qu'une seule ligne. – chim

1

Vous ne pouvez pas créer ces résultats avec une stricte requête SQL. Ce que vous essayez de produire s'appelle un tableau pivotant . De nombreux outils de création de rapports prennent en charge ce type de comportement, dans lequel vous devez sélectionner votre produit et votre catégorie, puis transformer la catégorie en colonne pivot.

Je crois que SQL Server Analysis Services prend également en charge des fonctionnalités de ce type, mais je n'ai aucune expérience avec SSAS.

1
SELECT p.name, cat_food.name, cat_flowers.name 
FROM 
    product p 
    left outer join Product_to_category pc_food 
    on p.id = pc_food.Prod_id 
    left outer join Category cat_food 
    on pc_food.Cat_id = cat_food.id 
    AND cat_food.name = 'Food' 
    left outer join Product_to_category pc_flowers 
    on p.id = pc_flowers.Prod_id 
    left outer join Category cat_flowers 
    on pc_flowers.Cat_id = cat_flowers.id 
    AND cat_flowers.Name = 'Flowers' 

Cela ne fonctionne que si vous connaissez le nombre de catégories possibles, pour les mettre dans des colonnes. C'est ainsi que fonctionne SQL (standard), le nombre de colonnes n'est pas dynamique.

1

La réponse de Seb m'a mis sur la bonne voie pour une solution de contournement. J'utilise Oracle et il a des fonctions qui émulent les group_concat de MYSQL. Voici un exemple. Cela ne génère pas de colonnes, et n'est donc pas aussi bon qu'une solution SQL pure, mais il est adapté à mes objectifs actuels.

with data as 
( 
    select 
    pc.id cat, 
    p.id prod, 
    row_number() over(partition by p.id order by pc.id) rn, 
    count(*) over (partition by p.id) cnt 
    from product_to_category pc, product p 
    where pc.product_id = p.id 
) 
select prod, ltrim(sys_connect_by_path(cat, ','), ',') cats 
    from data 
where rn = cnt 
start with rn = 1 connect by prior prod = prod and prior rn = rn - 1 
order by prod 

Cela génère des données telles que

 
PROD | CATS 
=========== 
284 | 12 
285 | 12 
286 | 9,12 

Je peux modifier la colonne ltrim (de sys_connect_by_path()) au besoin pour générer toutes les données dont j'ai besoin.

Questions connexes