Por favor, deja de usar ORDER BY RAND() . Solo para. Esta operación tiene una complejidad de n*log2(n) , lo que significa que el tiempo dedicado a la consulta crecería "
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 desea generar resultados aleatorios, cree un procedimiento almacenado que los genere. Algo como esto (código tomado de este artículo , que deberías leer):
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 ;
Y para usarlo, escribirías:
CALL get_rands(10);
SELECT * FROM rands;
En cuanto a ejecutarlo todo en el lado de PHP, debe dejar de usar el antiguo mysql_* API. Tiene más de 10 años y ya no se mantiene. La comunidad incluso ha proceso iniciado
por menospreciarlos. No debería haber más código nuevo escrito con mysql_* en 2012. En su lugar, debe usar PDO
o MySQLi
. En cuanto a cómo escribirlo (con 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);
}
Actualizar
Si el requisito es obtener no solo 10 resultados aleatorios, sino 10 resultados aleatorios ÚNICOS , entonces requeriría dos cambios en el PROCEDURE :
-
La tabla temporal debe hacer cumplir la unicidad de las entradas:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);También podría tener sentido recopilar solo ID y no los valores. Especialmente si lo que buscas son 10 artículos únicos, no solo etiquetas.
-
Cuando se encuentra la inserción de un valor duplicado, el
cntcontador no debe disminuir. Esto se puede asegurar agregando unHANDLER(antes de la definición deLOOP), que "atraparía" la advertencia planteada y ajustaría el contador:DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;