在大数据库上快速mysql随机加权选择

时间:2014-02-09 06:47:33

标签: mysql sql random

我建立了一个网站,我需要选择随机加权记录 来自数据库。

SQL : select one row randomly, but taking into account a weight

中有一段代码
SELECT t.*, RAND() * t.weight AS w 
FROM table t 
ORDER BY w DESC
LIMIT 1

它适用于小型记录样本。

当尝试接近1百万条记录时,它会变慢(1.3 - 1.8秒) 在我的本地机器上,我想我会在更大的机器上花更长的时间。

怎么可以优化? 是否有更好的方法可以随机选择加权记录?

我的尝试是定期计算权重,将它们存储在单独的表格中,选择随机数字编程并搜索最接近该数字的记录。

2 个答案:

答案 0 :(得分:1)

您可以根据权重对数据进行分区,然后随机选择一个分区。

确定要使用的分区:O(n)

SELECT Weight, FLOOR(RAND()*COUNT(*)) as Target 
FROM test 
GROUP BY Weight
ORDER BY RAND()*(Weight)*count(Weight)/100 DESC
LIMIT 1;

使用上一次查询中的权重和目标来获得结果:O(Log(n))

SELECT test.*
FROM test
WHERE Weight = $Weight
LIMIT $Target, 1

测试它:

CREATE TABLE `test` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `Weight` int(11) NOT NULL,
  PRIMARY KEY (`Id`),
  KEY `Weight` (`Weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


insert into test (Weight) ( select FLOOR(RAND()*1000) );

运行20次以创建100万个测试行:

insert into test (Weight) select FLOOR(rand()*1000) as Weight from test;

由于GROUP BY,第一个查询在O(n)中运行。如果您维护第二个表来跟踪每个权重的计数,则可以将其记录为log(n)运行时间。

在我的数据库中,测试表中包含800万行,第一个查询在(6.089 s)中运行,第二个查询在(0.001 s)中运行

答案 1 :(得分:0)

首先得到所有权重的总和,以便计算动态选择每一行的概率。

SELECT SUM(weight) FROM t;

我假设可以通过名为@TOTAL_WEIGHT

的mysql变量访问总和金额
SELECT t.* 
FROM t
WHERE RAND() <= (weight / @TOTAL_WEIGHT)
ORDER BY RAND()
LIMIT 1;

这可能会通过整个表格但仍然找不到匹配项,在这种情况下,您可能只是运行另一个查询来获取一个随机行。