Vänligen sluta använda ORDER BY RAND()
. Bara sluta. Denna operation har komplexiteten n*log2(n)
, vilket innebär att tiden som spenderas på frågan skulle växa "
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
Om du vill generera slumpmässiga resultat, skapa en lagrad procedur som genererar dem. Något liknande detta (koden hämtad från denna artikel , som du bör läsa):
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 ;
Och för att använda det skulle du skriva:
CALL get_rands(10);
SELECT * FROM rands;
När det gäller att köra allt på PHP-sidan, bör du sluta använda den gamla mysql_*
API. Den är mer än 10 år gammal och underhålls inte längre. Community har till och med startat processen
för att avvisa dem. Det borde inte finnas någon mer ny kod skriven med mysql_*
2012. Istället bör du använda PDO
eller MySQLi
. När det gäller hur man skriver det (med 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);
}
Uppdatera
Om kravet är att få inte bara 10 slumpmässiga resultat, utan faktiskt 10 UNIKA slumpmässiga resultat , då skulle det krävas två ändringar av PROCEDURE
:
-
Den temporära tabellen bör upprätthålla det unika med poster:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
Det kan också vara vettigt att bara samla ID:n och inte värdena. Speciellt om det du letar efter är 10 unika artiklar, inte bara taggar.
-
När du infogar ett dubblettvärde hittas
cnt
räknaren bör inte minska. Detta kan säkerställas genom att lägga till enHANDLER
(före definition avLOOP
), som skulle "fånga" den upphöjda varningen och justera räknaren:DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;