2009-04-20 5 views
5

私は、Product,Category、およびProduct_To_Categoryテーブルを持っているとしましょう。製品は複数のカテゴリに分類できます。あるテーブルを別のテーブルに複数回結合するSQL? (商品をカテゴリにマッピングする)

 
    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 

私は私の結果など

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

などを与えるSQLクエリを希望

私はこれを取得することができました最善の方法は、労働組合への束であります一緒に質問する。特定の製品の予想カテゴリ数ごとに1つのクエリ。

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) 

これにはいくつかの問題があります。

  • 最初に、予想されるカテゴリごとにこの結合を繰り返す必要があります。製品が8つのカテゴリーに入ることができるなら、私は8つの照会が必要です。
  • 第2に、カテゴリは同じ列に均等に配置されません。たとえば、商品に「食べ物、花」があり、「花、食べ物」がある場合もあります。

誰かがこれを行うより良い方法を知っていますか?また、このテクニックには技術的な名前がありますか?

答えて

8

私はあなたが使用しているRDBMSのか分からないが、MySQLであなたは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

GROUP BYを追加する必要があると思いますか? – meleyal

+0

絶対に!それを忘れてしまった、ありがとう! – Seb

+0

GROUP BYが指定されていない場合、このクエリはCOUNT(*)クエリが1つの行だけを返すのと同じ方法で1つの行を返します。 – chim

1

あなたは、厳密なSQLクエリでこれらの結果を作成することはできません。あなたが作り出そうとしているものは、ピボットテーブルと呼ばれています。多くのレポートツールでは、このような動作がサポートされています。ここでは、製品とカテゴリを選択し、そのカテゴリをピボット列に変換します。

SQL Server Analysis Servicesもこのような機能をサポートしていますが、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' 

可能なカテゴリの数を知っていて、列に入れることができる場合にのみ機能します。これが(標準的な)SQLの仕組みです。列の数は動的ではありません。

1

Sebの回答は、回避策のために適切なトラックに私を置く。私はOracleを使用しており、MySQLにはgroup_concatをエミュレートする関数があります。ここに例があります。これは列を生成しないため、純粋なSQLソリューションほど良くはありませんが、私の現在の目的には適しています。

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 

これは私が必要なものは何でもデータを生成するために、必要に応じて、私はLTRIM(SYS_CONNECT_BY_PATH())の列を編集することができ、そのような

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

などのデータを生成します。

関連する問題