J'ai une table en postgres qui contient quelques millions de lignes. J'ai vérifié sur Internet et j'ai trouvé ce qui suit
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
Cela fonctionne, mais c'est vraiment lent ... y a-t-il un autre moyen de faire cette requête, ou un moyen direct de sélectionner une ligne aléatoire sans lire toute la table? Au fait, «myid» est un entier mais il peut s'agir d'un champ vide.
Réponses:
Vous voudrez peut-être expérimenter
OFFSET
, comme dansSELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Le
N
est le nombre de lignes dansmytable
. Vous devrez peut-être d'abord faire unSELECT COUNT(*)
pour déterminer la valeur deN
.Mise à jour (par Antony Hatchkins)
Vous devez utiliser
floor
ici:Prenons un tableau de 2 lignes;
random()*N
génère0 <= x < 2
et, par exemple,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
retourne 0 lignes en raison de l'arrondi implicite à l'entier le plus proche.la source
SELECT COUNT(*)
?, je veux dire, ne pas utiliser toutes les valeurs du tableau mais seulement une partie d'entre elles?EXPLAIN SELECT ...
des valeurs différentes de N donne le même coût pour la requête, alors je suppose qu'il vaut mieux choisir la valeur maximale de N.PostgreSQL 9.5 a introduit une nouvelle approche pour une sélection d'échantillons beaucoup plus rapide: TABLESAMPLE
La syntaxe est
Ce n'est pas la solution optimale si vous ne voulez qu'une seule ligne sélectionnée, car vous devez connaître le COUNT du tableau pour calculer le pourcentage exact.
Pour éviter un COUNT lent et utiliser TABLESAMPLE rapide pour les tables de 1 ligne à des milliards de lignes, vous pouvez faire:
Cela n'a peut-être pas l'air si élégant, mais est probablement plus rapide que toutes les autres réponses.
Pour décider si vous souhaitez utiliser BERNULLI oder SYSTEM, lisez la différence sur http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/
la source
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?SELECT reltuples FROM pg_class WHERE relname = 'my_table'
pour l'estimation du nombre.J'ai essayé cela avec une sous-requête et cela a bien fonctionné. Offset, au moins dans Postgresql v8.4.4 fonctionne très bien.
la source
Vous devez utiliser
floor
:la source
random()*N
génère 0 <= x <2 et, par exemple,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
retourne 0 lignes en raison de l'arrondi implicite à l'int plus proche.order by random()
, à peu près comme3*O(N) < O(NlogN)
- les chiffres réels seront légèrement différents en raison des indices.WHERE myid NOT IN (1st-myid)
etWHERE myid NOT IN (1st-myid, 2nd-myid)
ne travaillerait depuis la décision est prise par le OFFSET. Hmmm ... Je suppose que je pourrais réduire N de 1 et 2 dans les deuxième et troisième SELECT.floor()
? Quel avantage offre-t-il?Consultez ce lien pour différentes options. http://www.depesz.com/index.php/2007/09/16/my-ought-on-getting-random-row/
Mettre à jour: (A. Hatchkins)
Le résumé de l'article (très) long est le suivant.
L'auteur énumère quatre approches:
1)
ORDER BY random() LIMIT 1;
- lent2)
ORDER BY id where id>=random()*N LIMIT 1
- non uniforme s'il y a des lacunes3) colonne aléatoire - doit être mise à jour de temps en temps
4) agrégat aléatoire personnalisé - méthode astucieuse, peut être lente: random () doit être généré N fois
et suggère d'améliorer la méthode n ° 2 en utilisant
5)
ORDER BY id where id=random()*N LIMIT 1
avec des requêtes ultérieures si le résultat est vide.la source
Le moyen le plus simple et le plus rapide de récupérer une ligne aléatoire est d'utiliser l'
tsm_system_rows
extension:Ensuite, vous pouvez sélectionner le nombre exact de lignes que vous souhaitez:
Ceci est disponible avec PostgreSQL 9.5 et versions ultérieures.
Voir: https://www.postgresql.org/docs/current/static/tsm-system-rows.html
la source
ORDER BY random() LIMIT 1;
devrait être assez rapide.Je suis venu avec une solution très rapide sans
TABLESAMPLE
. Beaucoup plus rapide queOFFSET random()*N LIMIT 1
. Il ne nécessite même pas de compte de table.L'idée est de créer un index d'expression avec des données aléatoires mais prévisibles, par exemple
md5(primary key)
.Voici un test avec des exemples de données de 1M lignes:
Résultat:
Cette requête peut parfois (avec une probabilité d'environ 1 / Number_of_rows) renvoyer 0 ligne, elle doit donc être vérifiée et réexécutée. De plus, les probabilités ne sont pas exactement les mêmes - certaines lignes sont plus probables que d'autres.
En comparaison:
Les résultats varient considérablement, mais peuvent être assez mauvais:
la source