MySQL置换
我有两张桌子。一个有产品,另一个有配套的捆绑包。我需要找出SQL,它允许我找到所有的组合,在这些组合中,我可以销售带有附加功能的产品MySQL置换,mysql,sql,Mysql,Sql,我有两张桌子。一个有产品,另一个有配套的捆绑包。我需要找出SQL,它允许我找到所有的组合,在这些组合中,我可以销售带有附加功能的产品 Products Name ID Bench 1 Extra Name ID Parent ID QTY undershelf 1 1 1 overshelf 2 1 1 wheels 3 1 1 我需
Products
Name ID
Bench 1
Extra
Name ID Parent ID QTY
undershelf 1 1 1
overshelf 2 1 1
wheels 3 1 1
我需要和输出表,该表显示了我可以销售产品的所有组合:
Bench
Bench + undershelf
Bench + undershelf + overshelf
Bench + overshelf
Bench + wheels
bench + wheels + overshelf and so one.
我想不出在mysql中有什么巧妙的方法可以做到这一点,但在脚本语言中这是非常容易的。在PHP中:
<?php
$extra = array('undershelf', 'overshelf', 'sheels');
$possible_combinations = pow(2, count($extra));
for ($i = 0; $i < $possible_combinations; $i++) {
$combo = array('Bench');
foreach ($extra as $j => $item) {
if ($i & pow(2, $j)) {
$combo[] = $item;
}
}
echo implode(' + ', $combo) . "\n";
}
虽然不简单,但完全可以在MySQL中实现。此示例最多可以处理5个附加项,并且易于扩展以获得更多:
CREATE TABLE products (name varchar(100), id int primary key);
INSERT INTO products (name, id) VALUES ('Bench', 1);
CREATE TABLE extra (name varchar(100), id int primary key, parent_id int references products.id, qty int);
INSERT INTO extra (name, id, parent_id, qty) VALUES
('undershelf', 1, 1, 1), ('overshelf', 2, 1, 1), ('wheels', 3, 1, 1);
CREATE TABLE boolean_values (x boolean);
INSERT INTO boolean_values VALUES (TRUE), (FALSE);
CREATE VIEW product_extras_interim_vw AS
SELECT p.id product_id, p.name product_name, e.id extra_id, e.name extra_name, x
FROM products p
JOIN extra e ON (e.parent_id = p.id)
CROSS JOIN boolean_values;
SELECT DISTINCT a.product_name
, CASE WHEN a.x THEN CONCAT(' + ', a.extra_name) END extra1
, CASE WHEN b.x THEN CONCAT(' + ', b.extra_name) END extra2
, CASE WHEN c.x THEN CONCAT(' + ', c.extra_name) END extra3
, CASE WHEN d.x THEN CONCAT(' + ', d.extra_name) END extra4
, CASE WHEN e.x THEN CONCAT(' + ', e.extra_name) END extra5
FROM product_extras_interim_vw a
LEFT JOIN product_extras_interim_vw b
ON ( a.product_id = b.product_id
AND b.extra_id > a.extra_id
AND a.x )
LEFT JOIN product_extras_interim_vw c
ON ( a.product_id = c.product_id
AND c.extra_id > b.extra_id
AND b.x )
LEFT JOIN product_extras_interim_vw d
ON ( a.product_id = d.product_id
AND d.extra_id > c.extra_id
AND c.x)
LEFT JOIN product_extras_interim_vw e
ON ( a.product_id = e.product_id
AND e.extra_id > d.extra_id
AND d.x)
ORDER BY product_name, extra1, extra2, extra3, extra4, extra5;
输出:
Bench
Bench + overshelf
Bench + overshelf + wheels
Bench + undershelf
Bench + undershelf + overshelf
Bench + undershelf + overshelf + wheels
Bench + undershelf + wheels
Bench + wheels
每个附加项都可以在bundle中,也可以不在bundle中,这使其成为二进制属性。 可视化组合的一种方法是创建一个单词,每个额外的单词都有一个位,1表示额外的单词在列表中,0表示不在列表中。 例如,如果以相反顺序读取二进制字符串,则Bench+undershelf+supershelf为110或011 生成每一个n位的组合将给出每一个n个额外的组合,它还将给出从0到2^n-1的每个数字 我们可以从这里返回: 1.生成从0到2的数字列表^n-1; 2.将数字转换为二进制,以列出附加值的组合 3.每一位都配上一个额外的 4.在bundle描述中连接附加项的名称
SELECT CONCAT(b.Name
, COALESCE(CONCAT(' + '
, GROUP_CONCAT(x.Name SEPARATOR ' + '))
, '')) Combination
FROM (SELECT p.Name, p.id
, LPAD(BIN(u.N + t.N * 10), e.Dim, '0') bitmap
FROM Products p
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) u
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) t
INNER JOIN (SELECT COUNT(1) Dim
, `Parent ID` pID
FROM Extra) E ON e.pID = p.ID
WHERE u.N + t.N * 10 < Pow(2, e.Dim)
) B
LEFT JOIN (SELECT @rownum := @rownum + 1 ID
, `Parent ID` pID
, Name
FROM Extra
, (Select @rownum := 0) r) X
ON x.pID = b.ID
AND SUBSTRING(b.bitmap, x.ID, 1) = '1'
GROUP BY b.Name, b.bitmap
这个查询最多可以处理六个额外数据,然后它需要另一个数字表,每三个额外数据一个数字
工作原理
子查询E计算额外的数量,这在C中用于将数字表u和t unit以及tens生成的元素限制为2^dim
该数字由BINu.N+t.N*10转换为二进制,然后左填充“0”以表示元素数,生成一个组合位图
要使用生成的位图,每个附加项都需要一个与其中某个位置匹配的假id,这就是子查询X的用途
这两个子查询由位图的第n个字符连接:如果字符为1,则额外的字符在捆绑包中,左连接以不丢失没有额外字符的产品。是否有最大数量的父子关系?如果是这样,您可以将表连接到它本身以获得所需的结果。如果没有,您将需要使用动态sql来完成此任务。@sgedes是的,我有最大数量的父子关系,并且在php中使用动态sql没有问题。但我不知道加入将如何给我带来我想要的结果。不要过于迂腐,但你对标题所示的排列不感兴趣,而是对问题文本中提到的组合感兴趣。在MySQL中提出了这个解决方案后,作为一个很好的练习,我意识到我不认为我会完全用SQL来实现它。最好使用PHP或Java之类的语言。如果您必须完全在数据库中执行此操作,那么使用存储过程可能是另一种选择。请在SQL FIDLE中发布此消息,以防任何人想要使用此代码:
SELECT CONCAT(b.Name
, COALESCE(CONCAT(' + '
, GROUP_CONCAT(x.Name SEPARATOR ' + '))
, '')) Combination
FROM (SELECT p.Name, p.id
, LPAD(BIN(u.N + t.N * 10), e.Dim, '0') bitmap
FROM Products p
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) u
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) t
INNER JOIN (SELECT COUNT(1) Dim
, `Parent ID` pID
FROM Extra) E ON e.pID = p.ID
WHERE u.N + t.N * 10 < Pow(2, e.Dim)
) B
LEFT JOIN (SELECT @rownum := @rownum + 1 ID
, `Parent ID` pID
, Name
FROM Extra
, (Select @rownum := 0) r) X
ON x.pID = b.ID
AND SUBSTRING(b.bitmap, x.ID, 1) = '1'
GROUP BY b.Name, b.bitmap