2008-10-17 23 views
42

¿Cuál es una manera rápida de seleccionar una fila aleatoria de una tabla grande de mysql?selección rápida de una fila aleatoria de una tabla grande en mysql

Estoy trabajando en php, pero estoy interesado en cualquier solución, incluso si está en otro idioma.

+2

posible duplicado de [MySQL seleccionar 10 filas aleatorias de 600K filas rápida] (http://stackoverflow.com/questions/4329396/mysql-select-10-random-rows-from-600k-rows-fast) –

Respuesta

45

agarrar todos los identificadores, elige al azar de él, y recuperar la fila completa.

Si sabe que las identificaciones son secuenciales sin agujeros, puede simplemente tomar el máximo y calcular una identificación aleatoria.

Si hay agujeros aquí y allá, pero sobre todo los valores secuenciales, y que no se preocupan por una aleatoriedad ligeramente sesgada, agarran el valor máximo, calculan un ID y seleccione la primera fila con un id igual o por encima de la uno que calculó La razón del sesgo es que los siguientes huecos de identificación tendrán una mayor probabilidad de ser recogidos que los que siguen a otra identificación.

Si ordena de forma aleatoria, tendrá un terrible escaneo de tablas en sus manos, y la palabra rápida no se aplica a tal solución.

No hagas eso, ni deberías ordenar por un GUID, tiene el mismo problema.

3

Tal vez podría hacer algo como:

SELECT * FROM table 
    WHERE id= 
    (FLOOR(RAND() * 
      (SELECT COUNT(*) FROM table) 
     ) 
    ); 

Esto es asumiendo que sus números de identificación son todos secuencial sin huecos.

+0

En realidad es posible que desee CEIL en lugar de FLOOR, depende de si su ID comienza en 0 o 1 – davr

+0

. Supone que la expresión se almacena en caché y no se recalcula para cada fila. – BCS

+1

Hay espacios en la clave principal, ya que algunas filas se eliminan. – David

3

Agregue una columna que contenga un valor aleatorio calculado para cada fila, y utilícela en la cláusula de ordenamiento, limitando a un resultado en la selección. Esto funciona más rápido que tener el escaneo de tabla que causa ORDER BY RANDOM().

Actualización: Aún debe calcular algún valor aleatorio antes de emitir la declaración SELECT al recuperarla, por supuesto, p.

SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1 
+0

Pensé en eso. Agregue una nueva columna indexada y en la creación de filas, asígnele un int al azar. Pero el problema con esto es que estoy almacenar datos innecesarios y que todavía tendría que hacer algo más para conseguir realmente una fila al azar fuera de él, ya que los datos de columna al azar es estática. – David

+0

¿Cómo es que esto es -2, pero el de Cesar B es +17? Me parecen bastante iguales. –

+0

supuesto de que se "SELECT * FROM' 'foo' DONDE foo_rand'> = {} un valor aleatorio ORDER BY foo_rand LIMIT 1"? – haibuihoang

0

El clásico "SELECT ID FROM table ORDER BY RAND() LIMIT 1" está realmente bien.

Véase el extracto de seguimiento del manual de MySQL:

Si utiliza row_count LÍMITE con ORDER BY, MySQL termina la clasificación en cuanto se ha encontrado las primeras filas ROW_COUNT del resultado ordenada, en lugar de la clasificación de la resultado completo

+2

Pero todavía tiene que asignar un número aleatorio a todos y cada uno de los registros, ¿no? Lo pregunto porque esa explicación no tiene mucho sentido para mí: la forma en que se va a devolver primera N ordenadas filas si todo el conjunto de resultados no está ordenada: S –

+0

@igelkott, todavía hay un problema de rendimiento, supongo que no está bien – Unreality

1

Una manera fácil pero lento sería (bueno para tablas más bien pequeñas)

SELECT * from TABLE order by RAND() LIMIT 1 
+3

Esto producirá un valor aleatorio para todas las filas de la tabla, una clasificación, y luego agarrar una fila. Esto no es rápido –

+1

Es cierto. Aunque es rápido en tiempo de desarrollo. (y en tiempo de respuesta :-)).Lo dejaré aquí para los usuarios que no sean grandes y que lo necesiten –

+0

"smallish" puede ser sorprendentemente pequeño (me he encontrado con problemas con una tabla de entrada de 20k en un host virtual), y el seguimiento de este tipo de problema puede ser un dolor * real * en la espalda. Hazte un favor y usa un algoritmo adecuado desde el principio. – Creshal

0

Con un pedido, usted hará una tabla de escaneo completa. Su mejor si lo hace un recuento de selección (*) y más tarde obtiene una fila al azar = rownum entre el 0 y el último registro

1

En pseudocódigo:

sql "select id from table" 
store result in list 
n = random(size of list) 
sql "select * from table where id=" + list[n] 

Esto supone que id es un único (primaria) llave.

+0

Si los ID no cambian con frecuencia, puede incluso mantener la lista de IDs en memoria para hacer las cosas más rápido. –

+0

¿Qué pasa si hay mil millones de filas? Eso significa que tu lista de variables es enorme. –

36

Sabía que tenía que haber una manera de hacerlo en una sola consulta de una manera rápida.Y aquí está:

Una manera rápida y sin participación de código externo, felicitaciones a

http://jan.kneschke.de/projects/mysql/order-by-rand/

SELECT name 
    FROM random AS r1 JOIN 
     (SELECT (RAND() * 
        (SELECT MAX(id) 
         FROM random)) AS id) 
     AS r2 
WHERE r1.id >= r2.id 
ORDER BY r1.id ASC 
LIMIT 1; 
+1

Tenga en cuenta la compensación aquí, para estar seguro de obtener un resultado en el primer intento, es más probable que se seleccionen las teclas que están precedidas por espacios. p. ej., dados dos registros con las teclas 1 y 10, el registro con 10 como clave se seleccionará el 90% del tiempo. –

+1

Sí, puede obtener una mejor distribución si las claves están sin espacios vacíos y evitar las cláusulas WHERE y ORDER BY. Verifique el artículo, todo está muy bien explicado allí. No quería robar todo, por lo tanto no puse las otras consultas, pros y contras de cada uno. –

+0

Esta consulta de alguna manera no devuelve datos en algún momento cuando especifica algún parámetro adicional como WHERE r1.id> = r2.id AND r1.some_field = 1 mientras que some_field contiene data = 1. Alguna idea sobre como resolver esto? – Lomse

11

He aquí una solución que funciona con bastante rapidez, y se hace una mejor distribución aleatoria sin depender de Los valores de ID lindando o a partir de 1.

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

¿Cómo se obtiene la fila devuelta por esta consulta SQL usando PHP? Establecer '$ query' igual al anterior y luego hacer el habitual' mysql_query ($ query) 'no devuelve ningún resultado. Gracias. – ProgrammerGirl

+1

Eso es 1.5 escaneos de tabla - 1 para 'COUNT (*)' (asumiendo InnoDB), algo menos que un escaneo completo para 'OFFSET @ r'. Pero es excelente al ser aleatorio y no depender de las propiedades de una identificación. –

+0

@RickJames, a la derecha. Otra solución sería enumerar las filas con una nueva columna que está llena de enteros de serie. Entonces uno puede obtener el máximo con MAX() en lugar de COUNT(), y luego elegirlo por índice sin hacer frente a las brechas. Aunque esa solución requiere renumeración a medida que las filas van y vienen. –

30

MediaWiki utiliza un truco interesante (para especial de Wikipedia: la función aleatoria): J La tabla con los artículos tiene una columna adicional con un número aleatorio (generado cuando se crea el artículo). Para obtener un artículo al azar, genere un número aleatorio y obtenga el artículo con el siguiente valor más grande o más pequeño (no recuerdo cuál) en la columna de números aleatorios. Con un índice, esto puede ser muy rápido. (Y MediaWiki está escrito en PHP y desarrollado para MySQL.)

Este enfoque puede causar un problema si los números resultantes están mal distribuidos; IIRC, esto se ha corregido en MediaWiki, por lo que si decides hacerlo de esta manera, deberías echarle un vistazo al código para ver cómo se hace actualmente (probablemente regeneren periódicamente la columna de números aleatorios).

+1

Esta es una bella idea. ¿Hay algún artículo u otro recurso que detalle esto? –

+0

es una buena idea, pero para N los resultados deseados pueden no funcionar, supongo. Porque podría obtener menos resultados o el orden podría ser el mismo. – GorillaApe

+0

Es una buena idea. Pero en la consulta todavía tenemos que ordenar por la columna al azar, ¿verdad? Supongamos que la columna aleatoria es random_number, luego la consulta es como: "SELECT * FROM mytable WHERE random_number> $ rand ORDER BY random_number LIMIT 1". ¿Es mucho más rápido que ORDER BY RAND()? – haibuihoang

-1

método rápido y sucio:

SET @COUNTER=SELECT COUNT(*) FROM your_table; 

SELECT PrimaryKey 
FROM your_table 
LIMIT 1 OFFSET (RAND() * @COUNTER); 

La complejidad de la primera consulta es O (1) para las tablas MyISAM.

La segunda consulta acompaña a una tabla de exploración completa. Complejidad = O (n)

sucio y rápido método:

Mantener una tabla separada sólo para este propósito. También debe insertar las mismas filas en esta tabla cada vez que inserte en la tabla original. Suposición: Sin DELETE.

CREATE TABLE Aux(
    MyPK INT AUTO_INCREMENT, 
    PrimaryKey INT 
); 

SET @MaxPK = (SELECT MAX(MyPK) FROM Aux); 
SET @RandPK = CAST(RANDOM() * @MaxPK, INT) 
SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK); 

Si se permite que DELETEs,

SET @delta = CAST(@RandPK/10, INT); 

SET @PrimaryKey = (SELECT PrimaryKey 
        FROM Aux 
        WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta 
        LIMIT 1); 

la complejidad global es O (1).

0

que soy un poco nuevo en SQL, pero ¿qué hay de la generación de un número aleatorio en PHP y utilizando

SELECT * FROM the_table WHERE primary_key >= $randNr 

esto no resuelve el problema con los agujeros en la mesa.

Pero aquí hay un giro en lassevks sugerencia:

SELECT primary_key FROM the_table 

Use mysql_num_rows() en PHP crear un número aleatorio basado en el resultado anterior:

SELECT * FROM the_table WHERE primary_key = rand_number 

En una nota de cuán lento es SELECT * FROM the_table:
Creando un número aleatorio basado en mysql_num_rows() y luego moviendo el puntero de datos a ese punto mysql_data_seek(). ¿Qué tan lento será esto en grandes mesas con un millón de filas?

0

Tome un vistazo a this link por Jan Kneschke o this SO answer ya que ambos discuten la misma pregunta. La respuesta SO revisa varias opciones también y tiene algunas buenas sugerencias dependiendo de sus necesidades. Jan repasa todas las diversas opciones y las características de rendimiento de cada una. Él termina con la siguiente para el método más optimizado por el cual hacer esto dentro de un MySQL seleccionar:

SELECT name 
    FROM random AS r1 JOIN 
     (SELECT (RAND() * 
        (SELECT MAX(id) 
         FROM random)) AS id) 
     AS r2 
WHERE r1.id >= r2.id 
ORDER BY r1.id ASC 
LIMIT 1; 

HTH,

-Dipin

1

Para seleccionar varias filas al azar de una tabla dada (decir 'palabras'), nuestro equipo se le ocurrió esta belleza:

SELECT * FROM 
`words` AS r1 JOIN 
(SELECT MAX(`WordID`) as wid_c FROM `words`) as tmp1 
WHERE r1.WordID >= (SELECT (RAND() * tmp1.wid_c) AS id) LIMIT n 
1

si no elimina fila de esta tabla, la forma más eficiente es:

(si se conoce el ID mininum la omitida)

SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1 

$randId=mt_rand((int)$row['minId'], (int)$row['maxId']); 

SELECT id,name,... FROM table WHERE id=$randId LIMIT 1 
1

el fin de encontrar filas aleatorias de una tabla, no use ORDER BY RAND() porque obliga a MySQL a hacer una ordenación completa de archivos y solo luego a recuperar el número de filas límite requerido. Para evitar este tipo de archivo completo, use la función RAND() solo en la cláusula where. Se detendrá tan pronto como llegue al número requerido de filas. Ver http://www.rndblog.com/how-to-select-random-rows-in-mysql/

0

me encontré con el problema de que mis IDs no se secuencial. Lo que se me ocurrió con esto.

SELECT * FROM products WHERE RAND()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1 

Las filas devueltas son aproximadamente 5, pero la limitan a 1.

Si desea agregar otra cláusula WHERE se vuelve un poco más interesante. Supongamos que quiere buscar productos con descuento.

SELECT * FROM products WHERE RAND()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1 

Lo que tienes que hacer es asegurarse de que usted está volviendo lo suficientemente resultado que es por lo que tengo que fijar a 100. Tener un descuento DONDE < 0,2 cláusula en la sub consulta era 10 veces más lenta, por lo que es mejor volver más resultados y límite.

-2

SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;

0

que veo aquí una gran cantidad de solución. Uno o dos parece estar bien, pero otras soluciones tienen algunas limitaciones. Pero la siguiente solución funcionará para todas las situaciones

select a.* from random_data a, (select max(id)*rand() randid from random_data) b 
    where a.id >= b.randid limit 1; 

Aquí, identificación, no tiene por qué ser secuencial. Podría ser cualquier columna de clave principal/único/incremento automático.Por favor, consulte los siguientes Fastest way to select a random row from a big MySQL table

Gracias Zillur - www.techinfobest.com

0

Utilice la consulta siguiente para obtener la fila aleatoria

SELECT user_firstname , 
COUNT(DISTINCT usr_fk_id) cnt 
FROM userdetails 
GROUP BY usr_fk_id 
ORDER BY cnt ASC 
LIMIT 1 
0

En mi caso mi mesa tiene un id como clave primaria, incremento automático sin espacios, por lo que puede utilizar COUNT(*) o MAX(id) para obtener el número de filas.

he hecho este script para probar el funcionamiento más rápido:

logTime(); 
query("SELECT COUNT(id) FROM tbl"); 
logTime(); 
query("SELECT MAX(id) FROM tbl"); 
logTime(); 
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1"); 
logTime(); 

Los resultados son los siguientes:

  • Conde: 36.8418693542479 ms
  • Max: 0.241041183472 ms
  • Solicitar: 0.216960906982 ms

respuesta con el método de orden:

SELECT FLOOR(RAND() * (
    SELECT id FROM tbl ORDER BY id DESC LIMIT 1 
)) n FROM tbl LIMIT 1 

... 
SELECT * FROM tbl WHERE id = $result; 
0

he utilizado este y el trabajo ya estaba hecho la referencia de here

SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30; 
0

crear una función para hacer esto muy probablemente la mejor respuesta y la respuesta más rápida aquí!

Pros - Funciona incluso con lagunas y extremadamente rápido.

<? 

$sqlConnect = mysqli_connect('localhost','username','password','database'); 

function rando($data,$find,$max = '0'){ 
    global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL 
    if($data == 's1'){ 
    $query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1"); 

    $fetched_data = mysqli_fetch_assoc($query); 
     if(mysqli_num_rows($fetched_data>0){ 
     return $fetch_$data; 
     }else{ 
     rando('','',$max); // Start Over the results returned nothing 
     } 
    }else{ 
    if($max != '0'){ 
     $irand = rand(0,$max); 
     rando('s1',$irand,$max); // Start rando with new random ID to fetch 
    }else{ 

     $query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1"); 
     $fetched_data = mysqli_fetch_assoc($query); 
     $max = $fetched_data['id']; 
     $irand = rand(1,$max); 
     rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return 
    } 
    } 
} 

$your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY 
?> 

Por favor, tenga en cuenta este código como no se ha probado, pero es un concepto de trabajo para devolver las entradas al azar pese a los huecos .. Mientras que las diferencias no son lo suficientemente grande como para causar un problema de tiempo de carga.

Cuestiones relacionadas