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
进一步假设以下产品具有以下选项:
- 产品 1 = 选项 1、选项 2、选项 3
- 产品 2 = 选项 2、选项 3、选项 4
- 产品 3 = 选项 3、选项 4、选项 5
如何查询才能得到这样的结果:
- 产品 1,选项 1,选项 2,选项 3,NULL,NULL
- 产品 2,NULL,选项 2,选项 3,选项 4,NULL
- 产品 3,NULL,NULL,选项 3,选项 4,选项 5
我的选项实际上是一个嵌套树。他们有一个类别 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
您真的需要在数据库级别构建显示吗?
老实说,如果可行的话,我会避免头疼 运行:
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
假设一个 products
可以有 0 个或多个 options
-- 用以下 tables 实现:
products
- id
- name
options
- id
- name
product_options
- id
- product_id
- option_id
进一步假设以下产品具有以下选项:
- 产品 1 = 选项 1、选项 2、选项 3
- 产品 2 = 选项 2、选项 3、选项 4
- 产品 3 = 选项 3、选项 4、选项 5
如何查询才能得到这样的结果:
- 产品 1,选项 1,选项 2,选项 3,NULL,NULL
- 产品 2,NULL,选项 2,选项 3,选项 4,NULL
- 产品 3,NULL,NULL,选项 3,选项 4,选项 5
我的选项实际上是一个嵌套树。他们有一个类别 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
您真的需要在数据库级别构建显示吗?
老实说,如果可行的话,我会避免头疼 运行:
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