MySQL:选择随机输入,但是对某些条目的权重

Joh*_*ohn 36 php mysql random select database-table

我有一个MySQL表,里面有一堆条目,还有一个名为"Multiplier"的列.此列的默认(和最常见)值为0,但可以是任何数字.

我需要做的是随机从该表中选择一个条目.但是,行根据"乘数"列中的数字进行加权.值为0意味着它根本没有加权.值为1意味着它的加权值是两倍,就像条目在表中两次一样.值为2意味着它的加权值是其三倍,就像条目在表中三次一样.

我正在尝试修改我的开发人员已经给我的内容,很抱歉,如果设置没有多大意义.我可能会改变它,但希望保留尽可能多的现有表格设置.

我一直试图弄清楚如何使用SELECT和RAND(),但不知道如何进行加权.可能吗?

lim*_*mos 41

这家伙问同样的问题.他说和弗兰克一样,但是权重并没有出现在有人建议使用的评论中ORDER BY -LOG(1.0 - RAND()) / Multiplier,这在我的测试中给出了非常完美的结果.

(如果有任何数学家想解释为什么这是正确的,请赐教我!但它有效.)

缺点是您无法将权重设置为0以暂时禁用选项,因为您最终将除以零.但你总是可以用一个过滤掉它WHERE Multiplier > 0.

  • @KenArnold正如Crissistian Leonte在[同一主题]中的评论所指出的那样(http://www.kahunaburger.com/2008/10/13/selecting-random-weighted-records-from-mysql/)`1 - RAND()`实际上稍微"干净",因为它消除了你最终做"LOG(0)"返回"NULL"的微小机会.这是因为`RAND()`返回0 <= x <1.然而,两种解决方案都应返回可比较的结果. (5认同)
  • `1 - RAND()`相当于`RAND()`,它(理想情况下)在0和1之间均匀.`-LOG(RAND())/ weight`是指数,速率为`weight`.将世博会视为从现在开始直到您收到特定类型的电子邮件的时间,速率是每种电子邮件到达的速度.`LIMIT 1`只是选择下一封电子邮件. (4认同)
  • 这个解决方案是否意味着OP必须稍微改变他们的乘法器逻辑?他们最初说乘数“0”表示它没有加权,但您的解决方案意味着乘数“0”被排除在结果集中。OP 必须稍微改变他们的逻辑,以便乘数“1”意味着不加权,“2”意味着它在表中出现两次,等等。无论如何,这似乎更有意义,但只是想确认更改是必要的。 (2认同)
  • @ flyingL123真,好点.或者他们可以用"乘数+ 1"代替"乘数" (2认同)

Ali*_*Ali 8

为了获得更好的性能(特别是在大表上),首先索引权重列并使用此查询:

SELECT * FROM tbl WHERE id IN 
    (SELECT id FROM (SELECT id FROM tbl ORDER BY -LOG(1-RAND())/weight LIMIT x) t)
Run Code Online (Sandbox Code Playgroud)

使用了两个子查询,因为MySQL在第一个子查询中不支持LIMIT.

在40MB表上,通常的查询在我的i7机器上需要1 秒,这个需要0.04秒.

  • 你能解释一下子查询的意义吗?为什么不在最里面的子查询中使用`SELECT *`并取消其他两个呢?这就是通常查询的形式。 (2认同)
  • @concat这是因为SQL是如何工作的:在大表上执行命令时,它会加载整个数据,然后根据order by子句进行排序,但是这里的子查询仅适用于内存中可用的索引数据。请参阅以下测试:通常&gt; https://i.stack.imgur.com/006Ym.jpg,子查询&gt; https://i.stack.imgur.com/vXU8e.jpg突出显示了响应时间。 (2认同)

Fra*_*ens 6

不要使用0,1和2,而是使用1,2和3.然后您可以将此值用作乘数:

SELECT * FROM tablename ORDER BY (RAND() * Multiplier);
Run Code Online (Sandbox Code Playgroud)

  • 这实际上并没有给出正确的分布(正如我偶然发现的那样); 豪华轿车的答案确实如此. (3认同)
  • 或者只添加1:SELECT*FROM tablename ORDER BY(RAND()*(Multiplier + 1)); (2认同)

小智 0

无论你做什么,都是很糟糕的,因为它将涉及: * 将所有列的总“权重”作为一个数字(包括应用乘数)。* 获取 0 和总数之间的随机数。* 获取所有条目并运行它们,从随机数中扣除重量,并在用完条目时选择一个条目。

平均来说,你会沿着桌子的一半跑。性能 - 除非表很小,否则在内存中的 mySQL 之外执行 - 将会很慢。