Mysql
 sql >> Base de données >  >> RDS >> Mysql

Faire un moment / boucle pour obtenir 10 résultats aléatoires

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 "

entrées
    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 :

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

  2. 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 un HANDLER (avant la définition de LOOP ), qui "attraperait" l'avertissement déclenché et ajusterait le compteur :

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