SQL - 如何显示产品的选项,无论它们是否有?

SQL - how to display products' options, whether or not they have them?

假设一个 products 可以有 0 个或多个 options -- 用以下 tables 实现:

products
 - id
 - name

options
 - id
 - name

product_options
 - id
 - product_id
 - option_id

进一步假设以下产品具有以下选项:

如何查询才能得到这样的结果:

我的选项实际上是一个嵌套树。他们有一个类别 table 的外键(也是一个嵌套树)。最终,我需要能够执行此查询并按类别对结果进行分组。但是,我可能应该首先了解如何解决我的问题的这个简单版本。

更新 1: 我事先不知道选项可能是什么。此外,产品可能具有的选项数量没有限制。

你可以使用条件聚合,如果我理解正确的话:

select po.product_id,
       max(case when o.name = 'Option 1' then o.name end) as option_1,
       max(case when o.name = 'Option 2' then o.name end) as option_2,
       max(case when o.name = 'Option 3' then o.name end) as option_3,
       max(case when o.name = 'Option 4' then o.name end) as option_4,
       max(case when o.name = 'Option 5' then o.name end) as option_5
from product_options po join
     options o
     on po.option_id = o.id
group by po.product_id

如果您有未知数量的选项,您可以使用存储过程来动态创建可用于调整您的 table 的查询。像这样:

CREATE PROCEDURE display_options()
BEGIN
  SET @query = 'SELECT p.id, ';
  SET @query = CONCAT(@query, (SELECT GROUP_CONCAT(CONCAT('MAX(CASE WHEN o.name = ''', name, ''' THEN o.name END) AS `', name, '`')) FROM options ORDER BY id));
  SET @query = CONCAT_WS(' ', @query, 
                         'FROM products p',
                         'JOIN product_options po ON po.product_id = p.id',
                         'JOIN options o ON o.id = po.option_id',
                         'GROUP BY p.id');
  PREPARE stmt FROM @query;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
END

此过程将产生这样的查询(与@GordonLinoff 对您问题中的样本数据的回答基本相同):

SELECT p.name, 
       MAX(CASE WHEN o.name = 'Option 1' THEN o.name END) AS `Option 1`,
       MAX(CASE WHEN o.name = 'Option 2' THEN o.name END) AS `Option 2`,
       MAX(CASE WHEN o.name = 'Option 3' THEN o.name END) AS `Option 3`,
       MAX(CASE WHEN o.name = 'Option 4' THEN o.name END) AS `Option 4`,
       MAX(CASE WHEN o.name = 'Option 5' THEN o.name END) AS `Option 5` 
FROM products p 
JOIN product_options po ON po.product_id = p.id 
JOIN options o ON o.id = po.option_id 
GROUP BY p.name

然后可以准备和执行得到如下结果:

name        Option 1    Option 2    Option 3    Option 4    Option 5
Product 1   Option 1    Option 2    Option 3        
Product 2               Option 2    Option 3    Option 4    
Product 3                           Option 3    Option 4    Option 5

demo on dbfiddle

您真的需要在数据库级别构建显示吗?

老实说,如果可行的话,我会避免头疼 运行:

  SELECT p.*, o.*
    FROM product_options po
    JOIN products p
      ON p.id = po.product_id
    JOIN options o
      ON o.id = po.option_id
   WHERE ...
ORDER BY ...

然后您可以在遍历结果集时在应用程序级别整理出您想要的结构

经过如此多的试验,我发现下面的查询将 return 准确地得到您想要的结果,即使选项和产品可能会增加。

SELECT  CONCAT(P.productName, ",", GROUP_CONCAT(COALESCE(IF(P.optionId IN (product_options.option_id),P.optionName,NULL),"NULL") order by P.optionId)) AS result
FROM product_options
RIGHT JOIN (SELECT products.id as productId,  products.name as productName,options.id as optionId, options.name AS optionName from products,options) 
as P On P.productId = product_options.product_id AND P.optionId = product_options.option_id
GROUP BY P.productId