132 votes

Sélectionner une ligne aléatoire dans une table sqlite

J'ai une table sqlite avec le schéma suivant:

 CREATE TABLE foo (bar VARCHAR)
 

J'utilise cette table comme mémoire de stockage pour une liste de chaînes.

Comment sélectionner une ligne au hasard dans cette table?

238voto

astander Points 83138

Jetez un oeil à la sélection d'une ligne aléatoire à partir d'une table SQLite

 SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
 

32voto

Georges Dupéron Points 1078

Les solutions suivantes sont beaucoup plus rapides que anktastic (count(*) coûte beaucoup, mais si vous pouvez mettre en cache, alors la différence n'est pas si grand), qui lui-même est beaucoup plus rapide que le "order by random()" lorsque vous avez un grand nombre de lignes, mais ils ont quelques inconvénients.

Si votre rowids sont plutôt emballé (ie. quelques suppressions), vous pouvez effectuer les opérations suivantes :

select * from foo where rowid = (abs(random()) % (select max(rowid)+1 from foo));

Si vous avez des trous, parfois d'essayer de sélectionner une inexistant rowid, et select retourne un jeu de résultats vide. Si ce n'est pas acceptable, vous pouvez fournir une valeur par défaut comme ceci :

select * from foo where rowid = (abs(random()) % (select max(rowid)+1 from foo)) or rowid = (select max(rowid) from node) order by rowidid limit 1;

Cette deuxième solution n'est pas parfaite : la distribution de probabilité est plus élevée sur la dernière ligne (celle avec la plus haute rowid), mais si vous souvent ajouter des trucs à la table, il deviendra une cible en mouvement et de la distribution de probabilité devrait être beaucoup mieux.

Encore une autre solution, si vous sélectionnez des trucs aléatoires à partir d'une table avec beaucoup de trous, alors vous voudrez peut-être créer une table qui contient les lignes de la table d'origine triés dans un ordre aléatoire :

create table random_foo(foo_id);

Ensuite, periodicalliy, re-remplir le tableau random_foo

delete from random_foo;
insert into random_foo select id from foo;

Et pour sélectionner une ligne au hasard, vous pouvez utiliser ma première méthode (il n'y a pas de trous ici). Bien sûr, cette dernière méthode a des problèmes de concurrence d'accès, mais la re-construction de random_foo est un entretien de fonctionnement qui n'est pas susceptible de se produire très souvent.

Pourtant, encore une autre façon, que j'ai récemment trouvé sur une liste de diffusion, est de mettre un trigger sur supprimer pour déplacer la ligne avec la plus grande rowid dans le courant de ligne supprimée, de sorte que les trous sont de gauche.

Enfin, notez que le comportement de rowid et un integer primary key autoincrement n'est pas identique (avec rowid, lorsqu'une nouvelle ligne est insérée, max(rowid)+1 est choisi, wheras c'est plus de la valeur-jamais-vu+1 pour une clé primaire), de sorte que la dernière solution ne fonctionne pas avec un autoincrement dans random_foo, mais les autres méthodes.

17voto

ank Points 1909

Qu'en est-il de:

 SELECT COUNT(*) AS n FROM foo;
 

puis choisissez un nombre aléatoire m dans [0, n) et

 SELECT * FROM foo LIMIT 1 OFFSET m;
 

Vous pouvez même enregistrer le premier numéro ( n ) quelque part et le mettre à jour uniquement lorsque le nombre de bases de données change. De cette façon, vous n'avez pas à sélectionner SELECT COUNT à chaque fois.

13voto

Svetlozar Angelov Points 10583
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X