Domanda

Qual è un modo rapido per selezionare una riga casuale da una grande tabella mysql?

Sto lavorando a PHP, ma sono interessato a qualsiasi soluzione anche se è in un'altra lingua.

È stato utile?

Soluzione

Prendi tutti gli ID, scegline uno casuale e recupera l'intera riga.

Se sai che gli ID sono sequenziali senza buchi, puoi semplicemente prendere il massimo e calcolare un ID casuale.

Se ci sono buchi qua e là ma principalmente valori sequenziali e non ti interessa una casualità leggermente distorta, prendi il valore massimo, calcola un id e seleziona la prima riga con un id uguale o superiore a quello hai calcolato. Il motivo dell'inclinazione è che l'id che segue tali buchi avrà maggiori probabilità di essere scelto rispetto a quelli che seguono un altro id.

Se ordini a caso, avrai una terribile scansione dei tavoli tra le mani e la parola veloce non si applica a tale soluzione.

Non farlo, né dovresti ordinarlo da un GUID, ha lo stesso problema.

Altri suggerimenti

Sapevo che doveva esserci un modo per farlo in una sola query in modo rapido. Ed eccolo qui:

Un modo veloce senza coinvolgimento di codice esterno, complimenti a

http://jan.kneschke.de/projects/mysql/order -da-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 utilizza un trucco interessante (per lo speciale di Wikipedia: funzione casuale): la tabella con gli articoli ha una colonna aggiuntiva con un numero casuale (generato al momento della creazione dell'articolo). Per ottenere un articolo casuale, genera un numero casuale e ottieni l'articolo con il valore successivo più grande o più piccolo (non ricordare quale) nella colonna dei numeri casuali. Con un indice, questo può essere molto veloce. (E MediaWiki è scritto in PHP e sviluppato per MySQL.)

Questo approccio può causare un problema se i numeri risultanti sono mal distribuiti; IIRC, questo è stato risolto su MediaWiki, quindi se decidi di farlo in questo modo dovresti dare un'occhiata al codice per vedere come è attualmente fatto (probabilmente rigenerano periodicamente la colonna di numeri casuali).

Ecco una soluzione che funziona abbastanza rapidamente e ottiene una migliore distribuzione casuale senza che i valori ID siano contigui o che iniziano da 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;

Forse potresti fare qualcosa del genere:

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

Questo presuppone che i tuoi numeri ID siano tutti sequenziali senza lacune.

Aggiungi una colonna contenente un valore casuale calcolato a ciascuna riga e utilizzalo nella clausola d'ordine, limitando a un risultato al momento della selezione. Questo funziona più velocemente rispetto alla scansione della tabella che ORDER BY RANDOM() provoca.

Aggiornamento: devi comunque calcolare un valore casuale prima di emettere la SELECT dichiarazione al momento del recupero, ovviamente, ad es.

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

Un modo semplice ma lento sarebbe (buono per i tavoli più piccoli)

SELECT * from TABLE order by RAND() LIMIT 1

In pseudo codice:

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

Ciò presuppone che id sia una chiave univoca (primaria).

C'è un altro modo per produrre righe casuali usando solo una query e senza ordine di rand (). Implica variabili definite dall'utente. Vedi come produrre righe casuali da una tabella

Per trovare righe casuali da una tabella, non & # 8217; t usa ORDER BY RAND () perché forza MySQL a fare un ordinamento completo dei file e solo allora per recuperare il numero di righe limite richiesto. Per evitare questo ordinamento completo del file, utilizzare la funzione RAND () solo nella clausola where. Si fermerà non appena raggiungerà il numero richiesto di righe. Vedere http://www.rndblog.com/how-to- select-random-file-in-mysql /

se non si elimina la riga in questa tabella, il modo più efficiente è:

(se conosci l'id mininum, saltalo)

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

Per selezionare più righe casuali da una determinata tabella (dì "parole"), il nostro team ha inventato questa bellezza:

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

Il classico " SELEZIONA ID DA tabella ORDER BY RAND () LIMIT 1 " è effettivamente OK.

Vedi il seguente estratto dal manuale di MySQL:

Se usi LIMIT row_count con ORDER BY, MySQL termina l'ordinamento non appena ha trovato le prime righe_count del risultato ordinato, piuttosto che ordinare l'intero risultato.

Con un ordine farai una tabella di scansione completa. È meglio se fai un conteggio selezionato (*) e successivamente ottieni una riga casuale = rownum tra 0 e l'ultimo registro

Dai un'occhiata a questo link di Jan Kneschke o questa risposta SO come entrambi discutono della stessa domanda. La risposta SO va anche su varie opzioni e ha alcuni buoni suggerimenti a seconda delle tue esigenze. Jan esamina tutte le varie opzioni e le caratteristiche prestazionali di ciascuna. Si conclude con il seguente per il metodo più ottimizzato con cui eseguire ciò all'interno di una selezione 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

Sono un po 'nuovo di SQL, ma che ne dici di generare un numero casuale in PHP e di usare

SELECT * FROM the_table WHERE primary_key >= $randNr

questo non risolve il problema con buchi nella tabella.

Ma ecco una svolta al suggerimento di Lassevks:

SELECT primary_key FROM the_table

Usa mysql_num_rows () in PHP per creare un numero casuale basato sul risultato precedente:

SELECT * FROM the_table WHERE primary_key = rand_number

In una nota a margine quanto è lento SELECT * FROM the_table:
Creazione di un numero casuale basato su mysql_num_rows() e quindi spostamento del puntatore dati su quel punto mysql_data_seek(). Quanto sarà lento su tabelle di grandi dimensioni con un milione di righe?

Ho riscontrato un problema in cui i miei ID non erano sequenziali. Quello che mi è venuto in mente.

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

Le righe restituite sono circa 5, ma le limito a 1.

Se vuoi aggiungere un'altra clausola WHERE diventa un po 'più interessante. Supponiamo che tu voglia cercare prodotti scontati.

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

Quello che devi fare è assicurarti di restituire un risultato sufficiente, motivo per cui l'ho impostato su 100. Avere uno sconto WHERE < .2 nella sottoquery era 10 volte più lento, quindi è meglio tornare più risultati e limiti.

Vedo qui molte soluzioni. Uno o due sembra ok ma altre soluzioni hanno alcuni vincoli. Ma la seguente soluzione funzionerà per tutte le situazioni

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

Qui, id, non deve essere sequenziale. Potrebbe essere qualsiasi chiave primaria / unica / colonna di incremento automatico. Si prega di consultare il seguente Il modo più veloce per selezionare una riga casuale da una grande tabella MySQL

Grazie Zillur - www.techinfobest.com

Utilizzare la query seguente per ottenere la riga casuale

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

Nel mio caso la mia tabella ha un ID come chiave primaria, incremento automatico senza spazi vuoti, quindi posso usare COUNT(*) o MAX(id) per ottenere il numero di righe.

Ho creato questo script per testare l'operazione più veloce:

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();

I risultati sono:

  • Conteggio: 36.8418693542479 ms
  • Max: 0.241041183472 ms
  • Ordine : 0.216960906982 ms

Rispondi con il metodo dell'ordine:

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

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

L'ho usato e il lavoro è stato fatto il riferimento da qui

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

Crea una funzione per farlo molto probabilmente la risposta migliore e la risposta più veloce qui!

Pro: funziona anche con gap ed estremamente veloce.

<?

$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
?>

Tieni presente che questo codice non è stato testato ma è un concetto funzionante per restituire voci casuali anche con spazi vuoti. Finché gli spazi non sono abbastanza grandi da causare un problema di tempo di caricamento.

Metodo rapido e sporco:

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

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

La complessità della prima query è O (1) per le tabelle MyISAM.

La seconda query accompagna una scansione completa della tabella. Complessità = O (n)

Metodo sporco e rapido:

Mantieni una tabella separata solo a questo scopo. È inoltre necessario inserire le stesse righe in questa tabella ogni volta che si inserisce nella tabella originale. Presupposto: nessuna eliminazione.

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);

Se DELETE è consentito,

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

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

La complessità complessiva è O (1).

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top