2010-04-25 11 views
5
mysql> EXPLAIN SELECT * FROM urls ORDER BY RAND() LIMIT 1; 
+----+-------------+-------+------+---------------+------+---------+------+-------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra       | 
+----+-------------+-------+------+---------------+------+---------+------+-------+---------------------------------+ 
| 1 | SIMPLE  | urls | ALL | NULL   | NULL | NULL | NULL | 62228 | Using temporary; Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+-------+---------------------------------+ 

Lo anterior no califica como eficiente, ¿cómo debo hacerlo correctamente?¿Cómo selecciono un registro aleatorio de manera eficiente en MySQL?

ACTUALIZACIÓN

Parece utilizando la solución indicada en la respuesta todavía no ayuda:

mysql> explain SELECT * 
    -> FROM (
    ->   SELECT @cnt := COUNT(*) + 1, 
    ->     @lim := 10 
    ->   FROM urls 
    ->  ) vars 
    -> STRAIGHT_JOIN 
    ->   (
    ->   SELECT r.*, 
    ->     @lim := @lim - 1 
    ->   FROM urls r 
    ->   WHERE (@cnt := @cnt - 1) 
    ->     AND RAND(20090301) < @lim/@cnt 
    ->  ) i; 
+----+-------------+------------+--------+---------------+------+---------+------+-------+------------------------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra      | 
+----+-------------+------------+--------+---------------+------+---------+------+-------+------------------------------+ 
| 1 | PRIMARY  | <derived2> | system | NULL   | NULL | NULL | NULL |  1 |        | 
| 1 | PRIMARY  | <derived3> | ALL | NULL   | NULL | NULL | NULL | 10 |        | 
| 3 | DERIVED  | r   | ALL | NULL   | NULL | NULL | NULL | 62228 | Using where     | 
| 2 | DERIVED  | NULL  | NULL | NULL   | NULL | NULL | NULL | NULL | Select tables optimized away | 
+----+-------------+------------+--------+---------------+------+---------+------+-------+------------------------------+ 

Respuesta

4

Quassnoi ha escrito un post sobre la selección de filas al azar sin llevar a cabo una especie. Su ejemplo selecciona 10 filas al azar, pero puede adaptarlo para seleccionar solo una fila.

Si quiere que realmente rápido, entonces puede usar una aproximación que no será completamente uniforme o que algunas veces no devolverá una fila.

También puede utilizar un procedimiento almacenado para seleccionar una fila al azar rápidamente de Bill Karwin's post:

SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*) FROM mytable))); 
SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1'); 
PREPARE stmt1 FROM @sql; 
EXECUTE stmt1; 

Tenga en cuenta que esto va a funcionar mucho más rápido en MyISAM a InnoDB porque COUNT (*) es caro en InnoDB pero casi instantánea en MyISAM.

+0

Parece que no está funcionando en mi caso. – user198729

+0

@ user198729: Al mirar su código anterior, olvidó cambiar 'lim' de 10 a 1. Quizás no lo haya aclarado lo suficiente. –

+0

No importa, igual después de cambiar 10 a 1. Preste atención a la tercera línea de salida de explicación. – user198729

0

Bueno, si puede mover un poco de lógica a la capa de aplicación (y no malentendí su pregunta), entonces todo lo que necesita es generar ID aleatorio en su aplicación y luego realizar una selección simple para un registro identificado por esa clave . Todo lo que necesita saber es el recuento de registros. Ah, y si esa clave fue eliminada, obtenga la siguiente.

Cuestiones relacionadas