MySQL置换

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 我需

我有两张桌子。一个有产品,另一个有配套的捆绑包。我需要找出SQL,它允许我找到所有的组合,在这些组合中,我可以销售带有附加功能的产品

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