Veuillez arrêter d'utiliser ORDER BY RAND()
. Arrête. Cette opération a une complexité de n*log2(n)
, ce qui signifie que le temps passé sur la requête augmenterait "
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
Si vous souhaitez générer des résultats aléatoires, créez une procédure stockée qui les génère. Quelque chose comme ça (code tiré de cet article , que vous devriez lire) :
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 ;
Et pour l'utiliser, vous écririez :
CALL get_rands(10);
SELECT * FROM rands;
Quant à tout exécuter côté PHP, vous devez arrêter d'utiliser l'ancien mysql_*
API. Il a plus de 10 ans et n'est plus entretenu. La communauté a même processus commencé
pour les déprécier. Il ne devrait plus y avoir de nouveau code écrit avec mysql_*
en 2012. À la place, vous devriez utiliser PDO
ou MySQLi
. Quant à la façon de l'écrire (avec 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);
}
Mettre à jour
Si l'exigence est d'obtenir non seulement 10 résultats aléatoires, mais en réalité 10 résultats aléatoires UNIQUES , il faudrait alors deux changements à la PROCEDURE
:
-
La table temporaire doit appliquer l'unicité des entrées :
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
Il peut également être judicieux de collecter uniquement les ID et non les valeurs. Surtout si vous recherchez 10 articles uniques, pas seulement des balises.
-
Lorsque l'insertion d'une valeur en double est trouvée, le
cnt
le compteur ne doit pas diminuer. Cela peut être assuré en ajoutant unHANDLER
(avant la définition deLOOP
), qui "attraperait" l'avertissement déclenché et ajusterait le compteur :DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;