【问题标题】:SQL to join one table to another table multiple times? (Mapping products to categories)SQL多次将一个表连接到另一个表? (将产品映射到类别)
【发布时间】:2010-10-20 15:32:10
【问题描述】:

假设我有一个ProductCategoryProduct_To_Category 表。一个产品可以属于多个类别。

产品类别 Product_to_category 身份证 |姓名 ID |名称 Prod_id | Cat_id =================================================== == 1|玫瑰1|鲜花 1| 1 2|巧克力棒 2|食物2| 2 3|巧克力花3| 1 3| 2

我想要一个 SQL 查询,它会给我这样的结果

产品名称 |类别_1 |类别_2 |类别_3 ==================================================== ===== 玫瑰 |鲜花 | | 巧克力花 |鲜花 |食品 |

等等

我能够做到这一点的最好方法是将一堆查询合并在一起;对给定产品的每个预期类别数量进行一次查询。

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 个查询。
  • 第二,类别不统一放在同一列。例如,有时某个产品可能包含“食物,鲜花”,有时可能包含“鲜花,食物”。

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

【问题讨论】:

    标签: sql join


    【解决方案1】:

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

    【讨论】:

    • 没有 GROUP BY 这个查询只会返回一行,就像 COUNT(*) 查询只会返回一行一样。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:
      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 的工作方式,列数不是动态的。

      【讨论】:

        【解决方案4】:

        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
        

        这会生成诸如

        之类的数据 产品 |猫 =========== 284 | 12 285 | 12 286 | 9,12

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

        【讨论】:

          猜你喜欢
          • 2023-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-03
          • 1970-01-01
          • 1970-01-01
          • 2017-03-13
          相关资源
          最近更新 更多