2009-04-20 22 views
5

比方說,我有一個Product,CategoryProduct_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 

我想一個SQL查詢,這給了我一個結果,如

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

我已經能夠得到最好的方法就是工會一堆一起查詢;針對給定產品的每個預期數量的類別的一個查詢。

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個查詢。
  • 其次,類別不是統一放在同一列中。例如,有時一種產品可能有'食物,花卉',另一種'鮮花,食物'。

有沒有人知道更好的方法來做到這一點?此外,這種技術是否有技術名稱?

回答

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 2009-07-24 14:29:23

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

塞布的答案讓我走上了正確的軌道尋求解決方法。我使用的是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 

這產生的數據,例如

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

根據需要以產生I需要的任何數據I可以編輯LTRIM(SYS_CONNECT_BY_PATH())柱。