找到一个数字组合,它们等于mysql表中单个列的给定总和

时间:2015-06-30 10:31:56

标签: php mysql sql codeigniter phpmyadmin

我有一个包含字段id, max_post的MySQL表包。 max_post中的值包含

  1,2,3,4,5,10,20,30,40,50,100,200,500,1000,2000

我想找到最适合的套餐。例如,我输入230,然后它应该 消除一切,选择200和30包。我想使用SQL查询获得结果。

3 个答案:

答案 0 :(得分:1)

这是装箱问题的一个例子。基本上,解决它的唯一方法是明确的。

您可以通过显式连接获取所有此类组合的列表:

select p1.max_post, p2.max_post, p3.max_post, p4.max_post
from packages p1 left join
     packages p2
     on p1.max_post > p2.max_post left join
     packages p3
     on p2.max_post > p3.max_post left join
     packages p4
     on p3.max_post > p4.max_post
where (p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) +
       coalesce(p4.max_post, 0)
      ) = 230
order by (p2.max_post is null) desc,
         (p3.max_post is null) desc,
         (p4.max_post is null) desc

order by首先放入“较短”列表。如果您愿意,可以添加limit

注意:这实际上是在表格中的值中创建了四次笛卡尔积。随着表的大小增加,执行的时间也会增加。

答案 1 :(得分:1)

以下是Gordon查询的更正版本。主要是当有较小的值可用时,Gordon的查询缺少加入空记录。它为230找到的最佳匹配是200-20-5-4,因为最佳解决方案200-30-null-null甚至不在我们正在评估的集合中。这是因为p3永远不会外连接到200-30,因为表中的值小于30的记录。 (对于外连接意味着当没有匹配时添加空记录。)

select 
  p1.max_post, p2.max_post, p3.max_post, p4.max_post
from packages p1 
left join (select max_post from packages union all select null) p2
  on (p1.max_post > p2.max_post or p2.max_post is null)  
left join (select max_post from packages union all select null) p3 
  on (p2.max_post > p3.max_post or p3.max_post is null)  
left join (select max_post from packages union all select null) p4 
  on (p3.max_post > p4.max_post or p4.max_post is null)  
where p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) + coalesce(p4.max_post, 0) <= 230
order by 
  p1.max_post + coalesce(p2.max_post, 0) + coalesce(p3.max_post, 0) + coalesce(p4.max_post, 0) desc,
  (p2.max_post is null) desc,
  (p3.max_post is null) desc,
  (p4.max_post is null) desc
limit 1;

(可以通过向查询添加where max_post <= 230来稍微优化一下,因此值本身已经高于所需总和的记录将立即被解除。)

SQL小提琴:http://sqlfiddle.com/#!9/5f6d25/18

答案 2 :(得分:0)

符合您声明的483 =&gt;的替代方案200,100,50,40

SELECT
    target.max_post,
    p1.id,
    p1.max_post,
    p2.id,
    p2.max_post,
    p3.id,
    p3.max_post,
    p4.id,
    p4.max_post
FROM
(
    SELECT 483 AS max_post
)
    target
LEFT JOIN
    packages p1
        ON  p1.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p2
        ON  p2.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p3
        ON  p3.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post - p2.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )
LEFT JOIN
    packages p4
        ON  p4.id = (SELECT id
                       FROM packages
                      WHERE max_post <= target.max_post - p1.max_post - p2.max_post - p3.max_post
                   ORDER BY max_post DESC
                      LIMIT 1
                    )

它使用重复的相关子查询。对于非常小的packages表,笛卡尔积搜索可以更快(虽然不能解决上面提到的情况),对于我预期的任何合理大小的任何内容相关的子查询更快。

对于10个值的表格   - 笛卡尔积搜索了数百种组合(远远超过10 choose 4 = 210
  - 相关子查询搜索40个值(10x4)

对于100个值的表格   - Cartessian产品搜索数百万种组合(远远超过100 choose 4 = 3,921,225
  - 相关子查询搜索400个值(100x4)