Come prendo un campione casuale semplice ed efficiente in SQL? Il database in questione esegue MySQL; la mia tabella è di almeno 200.000 righe e voglio un semplice campione casuale di circa 10.000.
La risposta "ovvia" è:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Per le tabelle di grandi dimensioni, è troppo lento: richiede RAND()
ogni riga (che la mette già a O (n)) e le ordina, rendendola O (n lg n) nella migliore delle ipotesi. C'è un modo per farlo più velocemente di O (n)?
Nota : come sottolinea Andrew Mao nei commenti, se stai usando questo approccio su SQL Server, dovresti usare la funzione T-SQL NEWID()
, perché RAND () può restituire lo stesso valore per tutte le righe .
MODIFICA: 5 ANNI DOPO
Mi sono imbattuto di nuovo in questo problema con una tabella più grande e ho finito per utilizzare una versione della soluzione di @ ignorant, con due modifiche:
- Campiona le righe fino a 2-5 volte la dimensione del campione desiderata, a poco prezzo
ORDER BY RAND()
- Salva il risultato di
RAND()
in una colonna indicizzata a ogni inserimento / aggiornamento. (Se il tuo set di dati non è molto ricco di aggiornamenti, potresti dover trovare un altro modo per mantenere aggiornata questa colonna.)
Per prendere un campione di 1000 elementi di una tabella, conto le righe e campionamento il risultato fino a, in media, 10.000 righe con la colonna frozen_rand:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(La mia implementazione effettiva richiede più lavoro per assicurarmi di non sottocampionare e per racchiudere manualmente rand_high, ma l'idea di base è "tagliare a caso il tuo N a poche migliaia")
Sebbene ciò comporti alcuni sacrifici, mi consente di campionare il database utilizzando una scansione dell'indice, finché non è abbastanza piccolo da poterlo ORDER BY RAND()
nuovamente.
RAND()
restituisce lo stesso valore a ogni chiamata successiva.