MySQL选择3个随机行,其中三行的总和小于值

时间:2015-04-06 15:39:08

标签: php mysql

我正在尝试从表格中选择三个随机行,其中合并的item_price列小于所需的数量。

想象一下你有一个<input>一美元金额。输入美元金额时,数据库会返回三个随机项,其中组合价格小于或等于您输入的金额。

如果我输入300美元,你可以购买这三件商品,150美元,100美元和50美元。我创建的查询很难返回符合此条件的三个项目。

SELECT t1.item_id, t1.item_price
FROM items t1
INNER JOIN items t2 ON ( t1.item_id = t2.item_id )
GROUP BY t1.item_id, t1.item_name, t1.item_price
HAVING SUM( t2.item_price ) <=300
ORDER BY RAND( )
LIMIT 3 

我认为这会奏效,但我认为这只是一个巧合。它似乎只返回价格低于300美元,而不是总价不到300美元的任何三件商品。

我也试过这个问题:

SELECT t1.item_id, t1.item_price
FROM   items t1
JOIN   items t2 ON t2.item_id <= t1.item_id
WHERE  t2.item_price <= 500
GROUP  BY t1.item_id
HAVING SUM(t2.item_price) <= 500
ORDER  BY RAND()
LIMIT 3

再次,似乎首先工作,但后来开始以2000美元的价格退货。

如果在PHP中有更好的(甚至是牺牲性能)方式,我不介意。我只是认为查询不会那么困难。

一如既往,感谢任何人的帮助。

6 个答案:

答案 0 :(得分:4)

这是另一种解决方案:

SELECT t1.item_id as id1, t2.item_id as id2, t3.item_id as i3
FROM items t1, items t2, items t3
WHERE
t1.item_id <> t2.item_id and
t1.item_id <> t3.item_id and
t2.item_id <> t3.item_id and
(t1.item_price + t2.item_price + t3.item_price) <= 300
order by rand()
limit 1

您可以选择按最小总和

进行过滤

答案 1 :(得分:3)

你可以一步一步地做。假设我们有 $ 500 询问限制。首先获得数据库中的最低价格。

select MIN(item_price) from items

让我们说这是25.00所以对于我们的第一项,我们想要最大值500加2倍最小值(2 * 25 = 50)所以我检查第一项匹配小于或等于450美元

select item_id, item_price from items where item_price <= 450 order by rand() limit 1

这个项目现在可能是240美元,所以下一个查询是:

select item_id, item_price from items where item_price <= 140 order by rand() limit 1

下一个可能是50美元,所以下一个查询是:

select item_id, item_price from items where item_price <= 90 order by rand() limit 1

然后你去。

我知道,这是一个非常简单的解决方案,肯定可以有更好的解决方案,使用三连接和大型表上的随机排序会吞噬很多性能,结果是查询并不比运行这三个简单查询更好,如果表被正确索引,那么查询将像突发一样运行。

这样做也可以很好地控制返回的组合(即你可以扩展带有类别的项目并将查询减少到不同的类别,所以例如你可以结合技术+厨房+有趣的类别)。

由于我们都在这里学习,我们从不停止学习,我相信这个解决方案是灵活扩展功能的良好基础。如果你想使用单个查询,那么我建议让查询将大量可能的组合转储到表中,这样你就可以每天运行一次大量查询,当你想要选择一个组合时,你只需要查询预先渲染的随机表。

答案 2 :(得分:2)

您可以获得价格总和<= 300且

的所有商品三元组
SELECT a.item_id, a.item_price, b.item_id, b.item_price, c.item_id, c.item_price
  FROM items a 
       JOIN items b ON a.item_id < b.item_id
       JOIN items c ON b.item_id < c.item_id
 WHERE a.item_price + b.item_price + c.item_price <= 300

然后你可以按rand()排序并选择一个。

有关于在mysql中选择随机行的性能的讨论,你应该检查。如果项目表很大,则三重连接将是昂贵的。

修改

正如其他答案中所建议的那样,可以通过价格&lt; = 300过滤每个项目,并使用items.price上的索引来改进此查询。

答案 3 :(得分:1)

我能够通过这些查询和下面的PHP版本获得结果

SET @MaxAmount = 5;
SELECT FirstItem.id, SecondItem.id, ThirdItem.id, FirstItem.amount +  SecondItem.amount +  ThirdItem.amount as Total
FROM Items as FirstItem
CROSS JOIN Items as SecondItem  ON SecondItem.id <> FirstItem.id and FirstItem.amount + SecondItem.amount < @MaxAmount 
CROSS JOIN Items as ThirdItem ON ThirdItem.id <> FirstItem.id  and ThirdItem.id <> SecondItem.id and FirstItem.amount + SecondItem.amount + ThirdItem.amount < @MaxAmount
ORDER BY RAND()
LIMIT 3;

并且

SET @MaxAmount = 5;
SELECT FirstItem.id as id1, SecondItem.id as id2, ThirdItem.id as i3,  FirstItem.amount +  SecondItem.amount +  ThirdItem.amount as Total 
FROM Items FirstItem, Items SecondItem, Items ThirdItem
WHERE FirstItem.amount + SecondItem.amount < @MaxAmount
AND FirstItem.amount + SecondItem.amount  + ThirdItem.amount < @MaxAmount
AND SecondItem.id != FirstItem.id -- Prevent Same Id from showing up
AND ThirdItem.id != FirstItem.id  and ThirdItem.id != SecondItem.id
ORDER BY RAND()
LIMIT 3;

http://sqlfiddle.com/#!9/0e1c8/3

如果Items表相对较小,我只会这样做。您可以在PHP中执行此操作,方法是选择价格低于300的所有项目并生成k combinations(also named nCr) 3,然后使用过滤函数返回总和的值小于300的过滤函数。

$rows = $db->query("Select FirstItem.amount as amount1, SecondItem.amount as amount2, ThirdItem.amount as amount3 (.. and also the ids) from Items where amount < 300");
$ncr = getCombinations($rows, 3);
$filtered = array_filter($ncr, function($row) { return $row['amount1'] + $row['amount2'] + $row['amount3'] < 300; })

答案 4 :(得分:1)

这是一个仅限SQL(MySQL风格)的解决方案:

SELECT i.*
FROM items i
CROSS JOIN
    (SELECT CONCAT('^(', t1.item_id, '|', t2.item_id, '|', t3.item_id, ')$') AS regex
     FROM items t1
     CROSS JOIN items t2
     CROSS JOIN items t3
     WHERE t1.item_id < t2.item_id
       AND t2.item_id < t3.item_id 
       AND t1.item_price + t2.item_price + t3.item_price <= 300
     ORDER BY RAND()
     LIMIT 1) s
WHERE i.item_id REGEXP s.regex

对于大型结果集效率不高,因为它创建了满足总标准的3个项目的不同排列的子查询,然后随机选择其中一个。子查询将其结果作为正则表达式返回,以允许在外部查询中选择行。

请参阅SQL Fiddle demo

答案 5 :(得分:0)

通过查看Lashane的答案我得到的另一个解决方案是因为每件商品的价格不得高于总价。通过添加它可以有一些改进(尝试EXPLAIN到查询)。

SELECT t1.item_id as id1, t2.item_id as id2, t3.item_id as i3
FROM items t1, items t2, items t3
WHERE
t1.item_price <= 300 AND
t2.item_price <= 300 AND
t3.item_price <= 300 AND
t1.item_id <> t2.item_id AND
t1.item_id <> t3.item_id AND
t2.item_id <> t3.item_id AND
(t1.item_price + t2.item_price + t3.item_price) <= 300
ORDER BY RAND()
LIMIT 1
相关问题