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
cnt
contador 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;