Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Fazendo um while/loop para obter 10 resultados aleatórios


Por favor, pare de usar ORDER BY RAND() . Simplesmente pare. Esta operação tem complexidade de n*log2(n) , o que significa que o tempo gasto na consulta aumentaria "
    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 você deseja gerar resultados aleatórios, crie um procedimento armazenado, que os gere. Algo assim (código retirado este artigo , que você deve ler):
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 para usá-lo, você escreveria:
CALL get_rands(10);
SELECT * FROM rands;

Quanto à execução de tudo no lado do PHP, você deve parar de usar o antigo mysql_* API. Tem mais de 10 anos e não tem mais manutenção. A comunidade até iniciou o processo por desprezá-los. Não deve haver mais nenhum código novo escrito com mysql_* em 2012. Em vez disso, você deve usar PDO ou MySQLi . Quanto a como escrevê-lo (com 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);
}

Atualizar


Se o requisito for obter não apenas 10 resultados aleatórios, mas na verdade 10 resultados aleatórios ÚNICOS , isso exigiria duas alterações no PROCEDURE :

  1. A tabela temporária deve impor a exclusividade das entradas:
    CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
    

    Também pode fazer sentido coletar apenas IDs e não os valores. Especialmente se o que você procura são 10 artigos exclusivos, não apenas tags.

  2. Ao inserir um valor duplicado é encontrado, o cnt contador não deve diminuir. Isso pode ser garantido adicionando um HANDLER (antes da definição de LOOP ), que "pegaria" o aviso levantado e ajustaria o contador:
    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;