Pregunta

¿Cuál es una forma rápida de seleccionar una fila aleatoria de una tabla MySQL grande?

Estoy trabajando en php, pero me interesa alguna solución aunque sea en otro idioma.

¿Fue útil?

Solución

Tome todas las identificaciones, elija una aleatoria y recupere la fila completa.

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

Si hay agujeros aquí y allá, pero en su mayoría valores secuenciales, y no le importa una aleatoriedad ligeramente sesgada, tome el valor máximo, calcule una identificación y seleccione la primera fila con una identificación igual o superior a la calculaste La razón del sesgo es que la identificación que sigue a estos agujeros tendrá más posibilidades de ser elegida que las que siguen a otra identificación.

Si ordena al azar, tendrá un terrible escaneo de tabla en sus manos, y la palabra rápido no se aplica a tal solución.

No haga eso, ni debe ordenar por GUID, tiene el mismo problema.

Otros consejos

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

Una forma rápida sin implicació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;

MediaWiki usa un truco interesante (para Wikipedia: Especial: función aleatoria): 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 aleatorio, genere un número aleatorio y obtenga el artículo con el siguiente valor más grande o más pequeño (no recuerde qué) 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 solucionado en MediaWiki, por lo que si decide hacerlo de esta manera, debería echar un vistazo al código para ver cómo se hace actualmente (probablemente regeneren periódicamente la columna de números aleatorios).

Aquí hay una solución que se ejecuta bastante rápido y obtiene una mejor distribución aleatoria sin depender de que los valores de identificación sean contiguos o comiencen en 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;

Tal vez podrías hacer algo como:

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

Esto supone que sus números de identificación son todos secuenciales sin espacios.

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

Actualización: todavía necesita calcular algún valor aleatorio antes de emitir la declaración SELECT al recuperarlo, por supuesto, por ejemplo,

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

Una forma fácil pero lenta sería (bueno para tablas pequeñas)

SELECT * from TABLE order by RAND() LIMIT 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 una clave única (primaria).

Hay otra forma de producir filas aleatorias usando solo una consulta y sin orden por rand (). Implica variables definidas por el usuario. Vea cómo producir filas aleatorias a partir de una tabla

Para encontrar filas aleatorias de una tabla, no use & # 8217; t use ORDER BY RAND () porque obliga a MySQL a ordenar un archivo completo y solo entonces para 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 /

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

(si conoce la identificación mínima, omítala)

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

Para seleccionar varias filas aleatorias de una tabla dada (por ejemplo, 'palabras'), nuestro equipo ideó 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

El clásico " SELECCIONAR ID de la tabla ORDER BY RAND () LIMIT 1 " En realidad está bien.

Vea el siguiente extracto del manual de MySQL:

Si usa LIMIT row_count con ORDER BY, MySQL finaliza la clasificación tan pronto como encuentra las primeras filas de row_count del resultado ordenado, en lugar de ordenar todo el resultado.

Con un pedido, hará una tabla de exploración completa. Es mejor si hace un recuento selectivo (*) y luego obtiene una fila aleatoria = rownum entre 0 y el último registro

Eche un vistazo a este enlace de Jan Kneschke o esta respuesta SO como Ambos discuten la misma pregunta. La respuesta SO también incluye varias opciones y tiene algunas buenas sugerencias según sus necesidades. Jan repasa todas las opciones y las características de rendimiento de cada una. Él termina con lo siguiente para el método más optimizado para hacer esto dentro de una selección de MySQL:

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

Soy un poco nuevo en SQL pero ¿qué tal si generamos un número aleatorio en PHP y lo usamos?

SELECT * FROM the_table WHERE primary_key >= $randNr

Esto no soluciona el problema de los agujeros en la mesa.

Pero aquí hay un giro en la sugerencia de lassevk:

SELECT primary_key FROM the_table

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

SELECT * FROM the_table WHERE primary_key = rand_number

Por cierto, lo lento que es SELECT * FROM the_table:
Creando un número aleatorio basado en mysql_num_rows() y luego mover el puntero de datos a ese punto mysql_data_seek().¿Qué tan lento será esto en tablas grandes con, digamos, un millón de filas?

Me encontré con el problema donde mis ID no eran secuenciales. 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 lo limito a 1.

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

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

Lo que tiene que hacer es asegurarse de que está devolviendo un resultado suficiente, por eso lo configuré en 100. Tener una cláusula WHERE discount < .2 en la subconsulta fue 10 veces más lenta, por lo que es mejor regresar más resultados y límite.

Veo aquí mucha solución. Uno o dos parecen 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í, id, no es necesario que sea secuencial. Podría ser cualquier columna de clave principal / única / incremento automático. Consulte lo siguiente La forma más rápida de seleccionar una fila aleatoria de una gran tabla MySQL

Gracias Zillur - www.techinfobest.com

Use la consulta a continuación 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

En mi caso, mi tabla tiene una identificación como clave principal, incremento automático sin espacios, por lo que puedo usar COUNT(*) o MAX(id) para obtener el número de filas.

Hice este script para probar 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:

  • Cuenta: 36.8418693542479 ms
  • Máx .: 0.241041183472 ms
  • Pedido : 0.216960906982 ms

Responda con el método de pedido:

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

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

He usado esto y el trabajo estaba hecho la referencia de aquí

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

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

Pros: funciona incluso con brechas 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
?>

Tenga en cuenta que este código no se ha probado pero es un concepto funcional para devolver entradas aleatorias incluso con espacios en blanco. Siempre que los espacios no sean lo suficientemente grandes como para causar un problema de tiempo de carga.

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 una exploración completa de la tabla. Complejidad = O (n)

Método sucio y rápido:

Mantenga una tabla separada solo para este propósito. También debe insertar las mismas filas en esta tabla siempre que se 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 permiten 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 general es O (1).

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top