通过MySQL获取最不随机的记录集

时间:2014-01-17 13:05:04

标签: php mysql sql random

我正在尝试找到获取X随机记录的正确方法。此外,我想检查特定记录的使用情况,因此我不会像其他记录那样经常使用相同的随机记录。

我正在使用这3个表测试集合,一个表用于问题,一个表用于用户,一个表用于特定用户的服务问题。我希望用大约6000个问题来表现。

CREATE TABLE `questions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `question` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `served` (
  `user` int(11) NOT NULL DEFAULT '0',
  `question` int(11) NOT NULL DEFAULT '0',
  `count` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`user`,`question`),
  KEY `count` (`count`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nickname` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我发现从问题表中获取随机记录非常有效的查询如下:

SELECT  id, question
        FROM    (
                SELECT  @cnt := COUNT(*) + 1,
                        @lim := 10
                FROM    questions
                ) vars
        STRAIGHT_JOIN
                (
                SELECT  q.*,
                        @lim := @lim - 1
                FROM    questions q 
                WHERE   (@cnt := @cnt - 1)
                        AND RAND() < @lim / @cnt
                ) i

但是现在我想合并服务表,以确保从最少提供的问题中选择随机值。我想到的查询如下:

SELECT  id, question, count
        FROM    (
                SELECT  @cnt := COUNT(*) + 1,
                        @lim := 10
                FROM    questions
                ) vars
        STRAIGHT_JOIN
                (
                SELECT  q.*,
                        s.count,
                        @lim := @lim - 1
                FROM    questions q
                LEFT JOIN served s
                ON s.question = q.id
                WHERE   (@cnt := @cnt - 1)
                        AND RAND() < @lim / @cnt
                ORDER BY count ASC) i

这个查询的问题是它永远不会给出我的10个结果的限制+它永远不会给出我想要的记录。有人能把我推向正确的方向吗?

根据要求使用SQL小提琴来测试一些数据:http://sqlfiddle.com/#!2/3e5ed/5。我希望结果是10个问题,其中为用户1服务的“计数”是最少的(或不存在的现场)。

我最终使用了修改过的查询,它必须很快:

SELECT q.*, s1.count AS count_a, s2.count AS count_b
FROM questions q
LEFT JOIN served s1
ON (s1.question = q.id AND s1.user = 1)
LEFT JOIN served s2
ON (s2.question = q.id AND s2.user = 2)
WHERE q.categorie = 1
ORDER BY IFNULL(s1.count, 0) + IFNULL(s2.count, 0) + RAND()
LIMIT 10

1 个答案:

答案 0 :(得分:2)

人们在MySQL中获取随机记录的常用方法是:

获得10条随机记录:

SELECT * FROM questions
ORDER BY RAND()
LIMIT 10

当然,很明显,这会获取数据库中的所有记录,然后随机对它们进行排序以获得10条记录。它实际上并不只是从数据库中选择10个随机记录。但是,这种方法很容易防止重复。

现在,使用相同的技术,如果你想支持较少服务的问题,你可以这样做:

SELECT questions.* FROM questions
LEFT JOIN served
ON served.question = questions.id
ORDER BY IFNULL(served.count, 0) + RAND()
LIMIT 10

调整算法以改变您赞成服务次数的金额。

有更多高效的方法来获取随机记录,例如获取最大主键值(假设为auto_increment),然后对其使用RAND(),然后只选择一条记录。您可以使用LIMIT 1以防RAND()返回键中的间隙。但是,如果重复此过程以返回多个记录,则可能会有重复项。

如果您有连续的auto_increment值,您可以轻松利用PHP选择一组随机密钥,然后单独获取每条记录。如果它们不连续,则首先获取密钥列表。

第16章随机选择,SQL Antipatterns一书中详细介绍了这些技术。