Comment puis-je prendre un échantillon aléatoire simple et efficace en SQL? La base de données en question exécute MySQL; ma table comporte au moins 200 000 lignes et je veux un échantillon aléatoire simple d'environ 10 000.
La réponse «évidente» est de:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Pour les grandes tables, c'est trop lent: il appelle RAND()
chaque ligne (ce qui la met déjà à O (n)), et les trie, ce qui en fait au mieux O (n lg n). Existe-t-il un moyen de le faire plus rapidement que O (n)?
Remarque : comme Andrew Mao le souligne dans les commentaires, si vous utilisez cette approche sur SQL Server, vous devez utiliser la fonction T-SQL NEWID()
, car RAND () peut renvoyer la même valeur pour toutes les lignes .
EDIT: 5 ANS PLUS TARD
J'ai de nouveau rencontré ce problème avec une table plus grande et j'ai fini par utiliser une version de la solution de @ ignorant, avec deux ajustements:
- Échantillonnez les lignes jusqu'à 2 à 5 fois la taille de l'échantillon souhaitée, à un prix avantageux
ORDER BY RAND()
- Enregistrez le résultat de
RAND()
dans une colonne indexée à chaque insertion / mise à jour. (Si votre ensemble de données ne nécessite pas beaucoup de mises à jour, vous devrez peut-être trouver un autre moyen de garder cette colonne à jour.)
Pour prendre un échantillon de 1000 éléments d'une table, je compte les lignes et échantillonne le résultat jusqu'à, en moyenne, 10000 lignes avec la colonne 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
(Ma mise en œuvre réelle implique plus de travail pour m'assurer de ne pas sous-échantillonner et pour envelopper manuellement rand_high, mais l'idée de base est de "réduire au hasard votre N à quelques milliers")
Bien que cela fasse des sacrifices, cela me permet d'échantillonner la base de données en utilisant une analyse d'index, jusqu'à ce qu'elle soit suffisamment petite pour à ORDER BY RAND()
nouveau.
RAND()
renvoie la même valeur à chaque appel suivant.