Mysql
 sql >> Database >  >> RDS >> Mysql

Facendo un po' / loop per ottenere 10 risultati casuali

Per favore, smetti di usare ORDER BY RAND() . Semplicemente fermati. Questa operazione ha una complessità di n*log2(n) , il che significa che il tempo dedicato alla query aumenterebbe "

voci
    entries  |  time units
  -------------------------
         10  |         1     /* if this takes 0.001s */
      1'000  |       300
  1'000'000  |   600'000     /* then this will need 10 minutes */

Se vuoi generare risultati casuali, crea una stored procedure che li generi. Qualcosa del genere (codice tratto da questo articolo , che dovresti leggere):

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 ;

E per usarlo dovresti scrivere:

CALL get_rands(10);
SELECT * FROM rands;

Per quanto riguarda l'esecuzione sul lato PHP, dovresti smettere di usare l'antico mysql_* API. Ha più di 10 anni e non è più mantenuto. La community ha persino iniziato il processo per averli deprecati. Non dovrebbe esserci più nuovo codice scritto con mysql_* nel 2012. Invece dovresti usare PDO o MySQLi . Quanto a come scriverlo (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);
}

Aggiorna

Se il requisito è ottenere non solo 10 risultati casuali, ma in realtà 10 risultati casuali UNICI , quindi richiederebbe due modifiche alla PROCEDURE :

  1. La tabella temporanea dovrebbe rafforzare l'unicità delle voci:

    CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
    

    Potrebbe anche avere senso raccogliere solo ID e non i valori. Soprattutto se quello che stai cercando sono 10 articoli unici, non solo tag.

  2. Quando si inserisce un valore duplicato viene trovato il cnt contatore non dovrebbe diminuire. Questo può essere garantito aggiungendo un HANDLER (prima della definizione di LOOP ), che "cattura" l'avviso sollevato e regola il contatore:

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