MySQL selecciona 10 filas aleatorias de 600K filas rápidamente

¿Cuál es la mejor forma de escribir una consulta que seleccione 10 filas al azar de un total de 600k?

preguntado el 01 de diciembre de 10 a las 18:12

aquí está 8 técnicas; quizás uno funcione bien en su caso. -

26 Respuestas

Un gran poste que maneja varios casos, desde simples, hasta huecos, no uniformes con huecos.

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

Para el caso más general, así es como se hace:

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

Esto supone que la distribución de identificadores es igual y que puede haber huecos en la lista de identificadores. Consulte el artículo para obtener ejemplos más avanzados.

Respondido 06 Oct 14, 11:10

Sí, si tiene grandes lagunas en las identificaciones, entonces la posibilidad de que su identificación más baja sea elegida al azar es mucho menor que sus identificaciones altas. De hecho, la probabilidad de que la primera identificación después de la mayor brecha sea seleccionada sea en realidad la más alta. Por lo tanto, esto no es aleatorio por definición. - lukeocodes

¿Cómo se obtienen 10 filas aleatorias diferentes? ¿Tiene que establecer el límite en 10 y luego iterar 10 veces con mysqli_fetch_assoc($result) ? ¿O esos 10 resultados no son necesariamente distinguibles? - Adam

Random requiere la misma probabilidad de obtener cualquier resultado, en mi opinión. ;) - lukeocodes

El artículo completo aborda cuestiones como distribuciones desiguales y resultados repetidos. - brad szonye

específicamente, si tiene un espacio al comienzo de sus ID, se elegirá el primero (min / max-min) del tiempo. Para ese caso, un simple ajuste es MAX () - MIN () * RAND + MIN (), que no es demasiado lento. - Abominador de código

SELECT column FROM table
ORDER BY RAND()
LIMIT 10

No es la solución eficiente pero funciona

Respondido el 11 de enero de 18 a las 04:01

ORDER BY RAND() es relativamente lento - Mateusz Charytoniuk

Mateusz - prueba por favor, SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10 toma 0.0010, sin LIMIT 10 tomó 0.0012 (en esa tabla 3500 palabras). - arturo kushman

@zeusakm 3500 palabras no es tanto; el problema es que explota más allá de cierto punto porque MySQL tiene que ordenar TODOS los registros después de leer cada uno; una vez que esa operación golpea el disco duro, puede sentir la diferencia. - Jack

No quiero repetirme, pero de nuevo, eso es un escaneo completo de la tabla. En una mesa grande consume mucho tiempo y memoria y puede causar la creación y operación en una tabla temporal en el disco que es lento. - mate

Cuando me entrevisté con Facebook en 2010, me preguntaron cómo seleccionar un registro aleatorio de un archivo enorme de tamaño desconocido, en una lectura. Una vez que se le ocurre una idea, es fácil generalizarla para seleccionar varios registros. Entonces sí, ordenar todo el archivo es ridículo. Al mismo tiempo, es muy útil. Acabo de usar este enfoque para elegir 10 filas aleatorias de una tabla con más de 1,000,000 filas. Seguro, tuve que esperar un poco; pero solo quería tener una idea, cómo se ven las filas típicas en esta tabla ... - sergey orshansky

Consulta simple que tiene excelente desempeño y trabaja con huecos:

SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id

Esta consulta en una tabla de 200K toma Los 0.08s y la versión normal (SELECT * FROM tbl ORDER BY RAND () LIMIT 10) toma Los 0.35s en mi máquina.

Esto es rápido porque la fase de clasificación solo usa la columna de ID indexada. Puedes ver este comportamiento en la explicación:

SELECCIONAR * DE tbl ORDEN POR RAND () LÍMITE 10: Explicación simple

SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND () LIMIT 10) as t2 ON t1.id = t2.id enter image description here

Versión ponderada: https://stackoverflow.com/a/41577458/893432

Respondido 14 Abr '20, 11:04

estoy obteniendo consultas rápidas (alrededor de 0.5 segundos) con un CPU lenta, seleccionando 10 filas aleatorias en un tamaño de 400Gb no almacenado en caché de la base de datos MySQL de 2K registros. Mira aquí mi código: Selección rápida de filas aleatorias en MySQL

$time= microtime_float();

$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);

$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
   ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
    if($id_in) $id_in.=",$id";
    else $id_in="$id";
}
mysql_free_result($rquery);

$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
    logger("$id, $url",1);
}
mysql_free_result($rquery);

$time= microtime_float()-$time;

logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);

Respondido 24 Oct 20, 01:10

Dada mi tabla de más de 14 millones de registros, esto es tan lento como ORDER BY RAND() - Fabricio

@snippetsofcode En su caso, 400k de filas, puede usar "ORDER BY rand ()" simple. Tu truco con 3 consultas es inútil. Puede reescribirlo como "SELECT id, url FROM pages WHERE id IN (SELECT id FROM pages ORDER BY rand () LIMIT 10)" - Román Podlinov

Su técnica todavía hace un escaneo de mesa. Usar FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%'; para verlo. - Rick James

También intente ejecutar esa consulta en una página web de 200 req / s. La concurrencia te matará. - marki555

@RomanPodlinov se beneficia de esto sobre el llano ORDER BY RAND() es que solo ordena los identificadores (no filas completas), por lo que la tabla temporal es más pequeña, pero aún tiene que ordenarlos todos. - marki555

Es una consulta muy simple y de una sola línea.

SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;

Respondido el 13 de enero de 16 a las 14:01

FYI, order by rand() es muy lento si la mesa es grande - el mal Reiko

A veces, se acepta LENTO si quiero mantenerlo SIMPLE - user285594

La indexación debe aplicarse sobre la mesa si es grande. - Muhammad Azeem

La indexación no ayudará aquí. Los índices son útiles para cosas muy específicas y esta consulta no es una de ellas. - Andrés

Del libro:

Elija una fila aleatoria usando un desfase

Otra técnica más que evita los problemas encontrados en las alternativas anteriores es contar las filas del conjunto de datos y devolver un número aleatorio entre 0 y el recuento. Luego use este número como un desplazamiento al consultar el conjunto de datos

$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();

Utilice esta solución cuando no pueda asumir valores clave contiguos y necesite asegurarse de que cada fila tenga la misma posibilidad de ser seleccionada.

Respondido 24 Oct 20, 01:10

para mesas muy grandes, SELECT count(*) se vuelve lento. - hans z

Bueno, si no tiene espacios en blanco en sus claves y todas son numéricas, puede calcular números aleatorios y seleccionar esas líneas. pero probablemente este no será el caso.

Entonces, una solución sería la siguiente:

SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1

lo que básicamente asegurará que obtenga un número aleatorio en el rango de sus claves y luego seleccione la siguiente mejor que sea mayor. tienes que hacer esto 10 veces.

sin embargo, esto NO es realmente aleatorio porque lo más probable es que sus claves no se distribuyan de manera uniforme.

Es realmente un gran problema y no es fácil de resolver cumpliendo con todos los requisitos, el rand () de MySQL es lo mejor que puede obtener si realmente desea 10 filas aleatorias.

Sin embargo, existe otra solución que es rápida pero que también tiene una compensación cuando se trata de aleatoriedad, pero que puede ser más adecuada para usted. Lea sobre esto aquí: ¿Cómo puedo optimizar la función ORDER BY RAND () de MySQL?

La pregunta es qué tan aleatorio necesitas que sea.

¿Puede explicarme un poco más para que pueda darle una buena solución?

Por ejemplo, una empresa con la que trabajé tenía una solución en la que necesitaban una aleatoriedad absoluta extremadamente rápido. Terminaron rellenando previamente la base de datos con valores aleatorios que se seleccionaron de forma descendente y se establecieron nuevamente en diferentes valores aleatorios.

Si casi nunca actualiza, también puede completar una identificación incremental para que no tenga espacios y solo pueda calcular claves aleatorias antes de seleccionar ... ¡Depende del caso de uso!

contestado el 23 de mayo de 17 a las 13:05

Hola Joe. En este caso particular, las claves no deben carecer de espacios, pero esto puede cambiar con el tiempo. Y mientras su respuesta funciona, generará las 10 filas aleatorias (siempre que escriba el límite de 10) que son consecutivas y quería más aleatoriedad, por así decirlo. :) Gracias. - Francis

Si necesita 10, use algún tipo de unión para generar 10 filas únicas. - Juan

Eso es lo que dije. necesitas ejecutar eso 10 veces. combinarlo con unión es una forma de ponerlo en una consulta. vea mi apéndice hace 2 minutos. - El surrican

@TheSurrican, esta solución se ve genial pero es altamente defectuoso. Intenta insertar solo . muy grande Id y todos las sus consultas aleatorias le devolverán esa Id. - marcapasos

FLOOR(RAND()*MAX(id)) está predispuesto a devolver identificadores más grandes. - Rick James

Cómo seleccionar filas aleatorias de una tabla:

De aquí: Seleccionar filas aleatorias en MySQL

Una mejora rápida con respecto al "escaneo de tablas" es usar el índice para recoger identificadores aleatorios.

SELECT *
FROM random, (
        SELECT id AS sid
        FROM random
        ORDER BY RAND( )
        LIMIT 10
    ) tmp
WHERE random.id = tmp.sid;

Respondido el 12 de Septiembre de 14 a las 16:09

Eso ayuda a algo para MyISAM, pero no para InnoDB (asumiendo que la identificación es la agrupada PRIMARY KEY). - Rick James

La consulta interna realiza un escaneo completo de la tabla y ordena los resultados. En realidad, la mayoría, tal vez todas, de las técnicas en ese enlace implican un escaneo completo. - Rick James

Revisé todas las respuestas y no creo que nadie mencione esta posibilidad en absoluto, y no estoy seguro de por qué.

Si desea la máxima simplicidad y velocidad, a un costo menor, me parece que tiene sentido almacenar un número aleatorio en cada fila en la base de datos. Solo crea una columna adicional, random_numbery establecerlo por defecto en RAND(). Cree un índice en esta columna.

Luego, cuando desee recuperar una fila, genere un número aleatorio en su código (PHP, Perl, lo que sea) y compárelo con la columna.

SELECT FROM tbl WHERE random_number >= :random LIMIT 1

Supongo que, aunque es muy bueno para una sola fila, para diez filas como el OP pidió, tendrías que llamarlo diez veces por separado (o proponer un ajuste inteligente que se me escape de inmediato)

Respondido 24 Oct 20, 01:10

En realidad, este es un enfoque muy agradable y eficiente. El único inconveniente es el hecho de que cambió el espacio por la velocidad, lo que, en mi opinión, parece un trato justo. - Tochukwu Nkemdilim

Gracias. Tuve un escenario en el que la tabla principal de la que quería una fila aleatoria tenía 5 millones de filas y bastantes uniones, y después de probar la mayoría de los enfoques en esta pregunta, este fue el problema con el que me decidí. Una columna adicional fue una compensación muy valiosa para mí. - código mono

¿Qué pasa si desea obtener 10 filas con "LIMIT 10"? Parece que las posibilidades ni siquiera. - xuxu

Como dije al final de mi respuesta @edwardaa, solo funciona realmente si quieres una sola fila. O si no le importa la sobrecarga de llamarlo varias veces. - código mono

Necesitaba una consulta para devolver una gran cantidad de filas aleatorias de una tabla bastante grande. Esto es lo que se me ocurrió. Primero obtenga el ID de registro máximo:

SELECT MAX(id) FROM table_name;

Luego sustituye ese valor en:

SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;

Donde max es el ID de registro máximo en la tabla yn es el número de filas que desea en su conjunto de resultados. La suposición es que no hay espacios en la identificación del registro, aunque dudo que afecte el resultado si los hubiera (aunque no lo he probado). También creé este procedimiento almacenado para que sea más genérico; pase el nombre de la tabla y el número de filas que se devolverán. Estoy ejecutando MySQL 5.5.38 en Windows 2008, 32GB, E3 dual de 5450GHz, y en una tabla con 17,361,264 filas es bastante consistente en ~ .03 seg / ~ 11 seg para devolver 1,000,000 filas. (los tiempos son de MySQL Workbench 6.1; también puede usar CEIL en lugar de FLOOR en la segunda instrucción de selección según sus preferencias)

DELIMITER $$

USE [schema name] $$

DROP PROCEDURE IF EXISTS `random_rows` $$

CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN

SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @t = CONCAT(
    'SELECT * FROM ',
    tab_name,
    ' WHERE id>FLOOR(RAND()*@max) LIMIT ',
    num_rows);

PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$

luego

CALL [schema name].random_rows([table name], n);

Respondido el 24 de Septiembre de 14 a las 14:09

Todas las mejores respuestas ya se han publicado (principalmente las que hacen referencia al enlace http://jan.kneschke.de/projects/mysql/order-by-rand/).

Quiero señalar otra posibilidad de aceleración: el almacenamiento en caché. Piense en por qué necesita obtener filas aleatorias. Probablemente desee mostrar alguna publicación aleatoria o un anuncio aleatorio en un sitio web. Si recibe 100 peticiones / s, ¿es realmente necesario que cada visitante obtenga filas aleatorias? Por lo general, está completamente bien almacenar en caché estas X filas aleatorias durante 1 segundo (o incluso 10 segundos). No importa si 100 visitantes únicos en el mismo 1 segundo obtienen las mismas publicaciones aleatorias, porque en el próximo segundo, otros 100 visitantes obtendrán un conjunto diferente de publicaciones.

Al usar este almacenamiento en caché, también puede usar algunas de las soluciones más lentas para obtener los datos aleatorios, ya que se obtendrán de MySQL solo una vez por segundo, independientemente de sus requisitos.

Respondido 07 Jul 15, 14:07

Mejoré la respuesta que tenía @Riedsio. Esta es la consulta más eficiente que puedo encontrar en una tabla grande distribuida uniformemente con lagunas (probado obteniendo 1000 filas aleatorias de una tabla que tiene> 2.6B filas).

(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)

Déjame desempacar lo que está pasando.

  1. @max := (SELECT MAX(id) FROM table)
    • Estoy calculando y guardando el máximo. Para tablas muy grandes, hay una ligera sobrecarga para calcular MAX(id) cada vez que necesitas una fila
  2. SELECT FLOOR(rand() * @max) + 1 as rand)
    • Obtiene una identificación aleatoria
  3. SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
    • Esto llena los vacíos. Básicamente, si selecciona al azar un número en los espacios, solo elegirá la siguiente identificación. Suponiendo que las brechas se distribuyan uniformemente, esto no debería ser un problema.

Hacer la unión le ayuda a encajar todo en una consulta para que pueda evitar hacer varias consultas. También le permite ahorrar la sobrecarga de calcular MAX(id). Dependiendo de su aplicación, esto puede importar mucho o muy poco.

Tenga en cuenta que esto solo obtiene los identificadores y los coloca en orden aleatorio. Si quieres hacer algo más avanzado, te recomiendo que hagas esto:

SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id

respondido 29 mar '17, 00:03

Necesito 30 registros aleatorios, así que debería cambiar LIMIT 1 a LIMIT 30 en todas partes en la consulta - Hassan

@Hassaan no deberías, ese cambio LIMIT 1 a LIMIT 30 obtendría 30 registros seguidos desde un punto aleatorio en la tabla. En su lugar, debería tener 30 copias del (SELECT id FROM .... parte en el medio. - hans z

Lo he intentado pero no me parece más eficiente entonces Riedsio respuesta . He intentado con 500 visitas por segundo a la página usando PHP 7.0.22 y MariaDB en centos 7, con Riedsio respuesta Obtuve más de 500 respuestas extra exitosas y luego tu respuesta. - Hassan

La respuesta de @Hassaan riedsio da 1 fila, esta le da n filas, así como también reduce la sobrecarga de E / S para realizar consultas. Es posible que pueda obtener filas más rápido, pero con más carga en su sistema. - hans z

Usé esto http://jan.kneschke.de/projects/mysql/order-by-rand/ publicado por Riedsio (utilicé el caso de un procedimiento almacenado que devuelve uno o más valores aleatorios):

   DROP TEMPORARY TABLE IF EXISTS rands;
   CREATE TEMPORARY TABLE rands ( rand_id INT );

    loop_me: LOOP
        IF cnt < 1 THEN
          LEAVE loop_me;
        END IF;

        INSERT INTO rands
           SELECT r1.id
             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;

        SET cnt = cnt - 1;
      END LOOP loop_me;

En el artículo resuelve el problema de las lagunas en identificadores que causan resultados no tan aleatorios manteniendo una tabla (usando disparadores, etc ... ver el artículo); Estoy resolviendo el problema agregando otra columna a la tabla, poblada con números contiguos, comenzando desde 1 (editar: esta columna se agrega a la tabla temporal creada por la subconsulta en tiempo de ejecución, no afecta su tabla permanente):

   DROP TEMPORARY TABLE IF EXISTS rands;
   CREATE TEMPORARY TABLE rands ( rand_id INT );

    loop_me: LOOP
        IF cnt < 1 THEN
          LEAVE loop_me;
        END IF;

        SET @no_gaps_id := 0;

        INSERT INTO rands
           SELECT r1.id
             FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
                  (SELECT (RAND() *
                                (SELECT COUNT(*)
                                   FROM random)) AS id)
                   AS r2
            WHERE r1.no_gaps_id >= r2.id
            ORDER BY r1.no_gaps_id ASC
            LIMIT 1;

        SET cnt = cnt - 1;
      END LOOP loop_me;

En el artículo puedo ver que hizo todo lo posible para optimizar el código; No tengo idea de si mis cambios impactan en el rendimiento o en qué medida, pero me funciona muy bien.

respondido 01 mar '20, 18:03

"No tengo ni idea de si mis cambios impactan en el rendimiento o en qué medida mis cambios", bastante. Para el @no_gaps_id no se puede utilizar ningún índice, así que si miras EXPLAIN para su consulta, tiene Using filesort y Using where (sin índice) para las subconsultas, en contraste con la consulta original. - Fabián Schmengler

Aquí hay un cambio de juego que puede ser útil para muchos;

Tengo una tabla con 200k filas con identificaciones secuenciales, Necesitaba elegir N filas aleatorias, así que opto por generar valores aleatorios basados ​​en el ID más grande en la tabla, creé este script para averiguar cuál es la operación más rápida:

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:

  • Contar: 36.8418693542479 ms
  • max: 0.241041183472 ms
  • Orden: 0.216960906982 ms

Según estos resultados, order desc es la operación más rápida para obtener la identificación máxima,
Aquí está mi respuesta a la pregunta:

SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
    SELECT FLOOR(RAND() * (
        SELECT id FROM tbl ORDER BY id DESC LIMIT 1
    )) n FROM tbl LIMIT 10) a

...
SELECT * FROM tbl WHERE id IN ($result);

FYI: Para obtener 10 filas aleatorias de una tabla de 200k, me tomó 1.78 ms (incluidas todas las operaciones en el lado de php)

contestado el 15 de mayo de 15 a las 12:05

Sugiero que aumente el LIMIT ligeramente: puede obtener duplicados. - Rick James

Esto es súper rápido y es 100% aleatorio incluso si tiene espacios.

  1. Cuenta el numero x de filas que tienes disponibles SELECT COUNT(*) as rows FROM TABLE
  2. Elija 10 números aleatorios distintos a_1,a_2,...,a_10 entre 0 y x
  3. Consulta tus filas así: SELECT * FROM TABLE LIMIT 1 offset a_i para i = 1, ..., 10

Encontré este truco en el libro Antipatrones SQL de Bill Karwin.

Respondido 21 Feb 19, 14:02

Estaba pensando en la misma solución, por favor dígame, ¿es más rápido que el otro método? - G. Adnano

@ G.Adnane no es más rápido ni más lento que la respuesta aceptada, pero la respuesta aceptada asume una distribución equitativa de las identificaciones. No puedo imaginar ningún escenario en el que esto pueda garantizarse. Esta solución está en O (1) donde la solución SELECT column FROM table ORDER BY RAND() LIMIT 10 está en O (nlog (n)). Entonces sí, esta es la solución en ayunas y funciona para cualquier distribución de identificadores. - Adam

no, porque en el enlace publicado para la solución aceptada, hay otros métodos, quiero saber si esta solución es más rápida que las otras, de otras maneras, podemos intentar encontrar otra, por eso estoy preguntando, de cualquier manera, +1 Para tu respuesta. Estaba usando lo mismo G. Adnano

hay un caso en el que desea obtener x número de filas, pero el desplazamiento va al final de la tabla que devolverá stackoverflow.com/a/59981772/10387008 - Eboubaker

@ZOLDIK parece que eliges las primeras 10 filas después del desplazamiento x. Yo diría que esta no es una generación aleatoria de 10 filas. En mi respuesta, debe ejecutar la consulta en el paso tres 10 veces, es decir, uno solo obtiene una fila por ejecución y no tiene que preocuparse si el desplazamiento está al final de la tabla. - Adam

Si solo tiene una solicitud de lectura

Combine la respuesta de @redsio con una tabla temporal (600K no es tanto):

DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;

Y luego tome una versión de @redsios Respuesta:

SELECT dt.*
FROM
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM tmp_randorder)) AS id)
        AS rnd
 INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
 INNER JOIN datatable AS dt on dt.id = rndo.data_id
 ORDER BY abs(rndo.id - rnd.id)
 LIMIT 1;

Si la mesa es grande, puede tamizar en la primera parte:

INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;

Si tiene muchas solicitudes de lectura

  1. Versión: Podrías quedarte con la mesa tmp_randorder persistente, llámalo datatable_idlist. Recrear esa tabla en determinados intervalos (día, hora), ya que también obtendrá huecos. Si su mesa se vuelve realmente grande, también puede rellenar los agujeros.

    seleccione l.data_id como completo de datatable_idlist l left join datatable dt en dt.id = l.data_id donde dt.id es nulo;

  2. Versión: proporcione a su conjunto de datos una columna de orden aleatorio, ya sea directamente en la tabla de datos o en una tabla adicional persistente datatable_sortorder. Indexe esa columna. Genere un valor aleatorio en su aplicación (lo llamaré $rand).

    select l.*
    from datatable l 
    order by abs(random_sortorder - $rand) desc 
    limit 1;
    

Esta solución discrimina las 'filas de borde' con el orden aleatorio más alto y el más bajo, así que reorganícelas en intervalos (una vez al día).

contestado el 07 de mayo de 14 a las 07:05

Otra solución simple sería clasificar las filas y buscar una de ellas al azar y con esta solución no necesitará tener ninguna columna basada en 'Id' en la tabla.

SELECT d.* FROM (
SELECT  t.*,  @rownum := @rownum + 1 AS rank
FROM mytable AS t,
    (SELECT @rownum := 0) AS r,
    (SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;

Puede cambiar el valor límite según su necesidad de acceder a tantas filas como desee, pero en su mayoría serían valores consecutivos.

Sin embargo, si no desea valores aleatorios consecutivos, puede obtener una muestra más grande y seleccionarla al azar. algo como ...

SELECT * FROM (
SELECT d.* FROM (
    SELECT  c.*,  @rownum := @rownum + 1 AS rank
    FROM buildbrain.`commits` AS c,
        (SELECT @rownum := 0) AS r,
        (SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d 
WHERE rank >= @cnt LIMIT 10000 
) t ORDER BY RAND() LIMIT 10;

respondido 10 nov., 15:05

Una forma que me parece bastante buena si hay una identificación generada automáticamente es usar el operador de módulo '%'. Por ejemplo, si necesita 10,000 registros aleatorios de 70,000, podría simplificar esto diciendo que necesita 1 de cada 7 filas. Esto se puede simplificar en esta consulta:

SELECT * FROM 
    table 
WHERE 
    id % 
    FLOOR(
        (SELECT count(1) FROM table) 
        / 10000
    ) = 0;

Si el resultado de dividir las filas de destino por el total disponible no es un número entero, tendrá algunas filas adicionales de las que solicitó, por lo que debe agregar una cláusula LIMIT para ayudarlo a recortar el conjunto de resultados de esta manera:

SELECT * FROM 
    table 
WHERE 
    id % 
    FLOOR(
        (SELECT count(1) FROM table) 
        / 10000
    ) = 0
LIMIT 10000;

Esto requiere un escaneo completo, pero es más rápido que ORDER BY RAND y, en mi opinión, más simple de entender que otras opciones mencionadas en este hilo. Además, si el sistema que escribe en la base de datos crea conjuntos de filas en lotes, es posible que no obtenga un resultado tan aleatorio como esperaba.

Respondido el 22 de junio de 16 a las 14:06

Ahora que creo que sí, si necesita filas aleatorias cada vez que lo llama, esto es inútil. Solo estaba pensando en la necesidad de obtener filas aleatorias de un conjunto para investigar un poco. Sigo pensando que el módulo es bueno para ayudar en el otro caso. Puede usar módulo como filtro de primer paso para reducir el costo de una operación ORDER BY RAND. - Nicolás Cohen

Si desea un registro aleatorio (sin importar si hay espacios entre los identificadores):

PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
        FLOOR(RAND() * COUNT(*))
    FROM `table_name`);

EXECUTE stmt USING @count;

Fuente: https://www.warpconduit.net/2011/03/23/selecting-a-random-record-using-mysql-benchmark-results/#comment-1266

Respondido 13 Jul 17, 00:07

Lo siguiente debe ser rápido, imparcial e independiente de la columna de identificación. Sin embargo, no garantiza que el número de filas devueltas coincidirá con el número de filas solicitadas.

SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)

Explicación: suponiendo que desee 10 filas de 100, entonces cada fila tiene una probabilidad de 1/10 de ser SELECCIONADA, lo que podría lograrse mediante WHERE RAND() < 0.1. Este enfoque no garantiza 10 filas; pero si la consulta se ejecuta suficientes veces, el número promedio de filas por ejecución será de alrededor de 10 y cada fila de la tabla se seleccionará de manera uniforme.

Respondido 30 Abr '19, 09:04

Creo que aquí hay una forma simple y aún más rápida, la probé en el servidor en vivo en comparación con algunas de las respuestas anteriores y fue más rápida.

 SELECT * FROM `table_name` WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table_name` ) ORDER BY id LIMIT 30; 

// Tomó 0.0014 segundos contra una tabla de 130 filas

SELECT * FROM `table_name` WHERE 1 ORDER BY RAND() LIMIT 30

// Tomó 0.0042 segundos contra una tabla de 130 filas

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

// Tomó 0.0040 segundos contra una tabla de 130 filas

Respondido el 18 de junio de 20 a las 04:06

Puede usar fácilmente un desplazamiento aleatorio con un límite

PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;

También puede aplicar una cláusula where como esta.

PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;

Probado en 600,000 filas (700 MB), la ejecución de consultas de tabla tomó ~ 0.016 segundos de disco duro.

EDITAR: El desplazamiento puede tomar un valor cercano al final de la tabla, lo que dará como resultado que la declaración de selección devuelva menos filas (o tal vez solo 1 fila), para evitar esto podemos verificar el offset de nuevo después de declararlo, así

SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;

Respondido 24 Oct 20, 01:10

Yo uso esta consulta:

select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10

tiempo de consulta: 0.016s

respondido 05 nov., 14:12

Tener PK como 1,2,9,15. por la consulta anterior obtendrá filas como 4, 7, 14, 11 que son insuficientes. - junaid atari

Así es como lo hago:

select * 
from table_with_600k_rows
where rand() < 10/600000
limit 10

Me gusta porque no requiere otras tablas, es simple de escribir y es muy rápido de ejecutar.

Respondido 15 Feb 13, 14:02

Eso es un escaneo de tabla completo y no usa ningún índice. Para mesas grandes y entornos ajetreados, eso es importante, no, no. - mate

Utilice la siguiente consulta simple para obtener datos aleatorios de una tabla.

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

Respondido 24 Feb 15, 07:02

Si desea usar cualquier declaración de unión y dónde puede usar el filtro. - Manoj

¿De qué parte de la consulta obtiene la aleatoriedad? - marki555

Supongo que esta es la mejor forma posible ...

SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no

Respondido 04 Abr '13, 17:04

Diablos, no, esa es una de las peores formas de obtener filas aleatorias de la tabla. Eso es escaneo de tabla completo + ordenación de archivos + tabla tmp = mal rendimiento. - mate

Además del rendimiento, también está lejos de ser perfectamente aleatorio; está ordenando por el producto de la identificación y un número aleatorio, en lugar de simplemente ordenar por un número aleatorio, lo que significa que las filas con identificadores más bajos estarán sesgadas para aparecer antes en su conjunto de resultados. - marca amery

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.