SQL将一个表连接到另一个表多次? (将产品映射到类别)

时间:2009-04-20 18:17:22

标签: sql join

假设我有ProductCategoryProduct_To_Category表格。产品可以分为多个类别。

    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个查询。
  • 其次,类别未统一放入相同的列中。例如,有时产品可能有'食物,花',另一种时候'花,食物'。

有谁知道更好的方法吗?此外,这种技术有技术名称吗?

4 个答案:

答案 0 :(得分:9)

我不知道您使用的是哪种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

答案 1 :(得分:1)

您无法使用严格的SQL查询创建这些结果。您要生成的内容称为数据透视表。许多报告工具都支持这种行为,您可以在其中选择产品和类别,然后将类别转换为数据透视列。

我相信SQL Server Analysis Services也支持这样的功能,但我对SSAS没有任何经验。

答案 2 :(得分: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的工作原理,列数不是动态的。

答案 3 :(得分: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

这会生成

等数据
PROD | CATS
===========
284  |   12
285  |   12
286  | 9,12

我可以根据需要编辑ltrim(sys_connect_by_path())列,以生成我需要的任何数据。