请停止使用ORDER BY RAND()。停下来。该操作的复杂度为n*log2(n),这意味着查询所花费的时间会增加“
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
如果您想生成随机结果,请创建一个存储过程来生成它们。像这样的东西(代码取自this article,你应该阅读):
DELIMITER $$
DROP PROCEDURE IF EXISTS get_rands$$
CREATE PROCEDURE get_rands(IN cnt INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET cnt = cnt - 1;
INSERT INTO rands
SELECT tags.tagname
FROM tags
JOIN (SELECT (RAND()*(SELECT MAX(tags.id) FROM tags)) AS id) AS choices
WHERE tags.id >= choices.id
LIMIT 1;
END LOOP loop_me;
END$$
DELIMITER ;
要使用它,你会写:
CALL get_rands(10);
SELECT * FROM rands;
至于在 PHP 端执行它,你应该停止使用古老的mysql_* API。它已经使用了 10 多年,不再维护。社区甚至有 begun process 弃用它们。 2012 年不应再有使用mysql_* 编写的新代码。相反,您应该使用PDO 或MySQLi。至于怎么写(用PDO):
// creates DB connection
$connection = new PDO('mysql:host=localhost;dbname=mydb;charset=UTF-8',
'username', 'password');
$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// executes the procedure and creates select statement
$connection->exec('CALL get_rands(10)');
$statement = $connection->query('SELECT * FROM rands');
// performs query and collects all the info
if ($statement->execute())
{
$tags = $statement->fetchAll(PDO::FETCH::ASSOC);
}
更新
如果要求不仅获得 10 个随机结果,而且实际上是 10 个唯一的随机结果,则需要对 PROCEDURE 进行两次更改:
-
临时表应该强制条目的唯一性:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
只收集 ID 而不是收集值也可能有意义。特别是如果您正在寻找 10 篇独特的文章,而不仅仅是标签。
-
发现插入重复值时,cnt 计数器不应减少。这可以通过添加HANDLER(在LOOP 的定义之前)来确保,这将“捕获”引发的警告并调整计数器:
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;