sql >> Base de Datos >  >> RDS >> Mysql

Haciendo un while/bucle para obtener 10 resultados aleatorios

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 :

  1. 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.

  2. Cuando se encuentra la inserción de un valor duplicado, el cnt contador no debe disminuir. Esto se puede asegurar agregando un HANDLER (antes de la definición de LOOP ), que "atraparía" la advertencia planteada y ajustaría el contador:

    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;