web-dev-qa-db-fra.com

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 stockage pour une liste de chaînes.

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

107
Alex_coder

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

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

Les solutions suivantes sont beaucoup plus rapides que celles d'Anktastic (le compte (*) coûte cher, mais si vous pouvez le mettre en cache, la différence ne devrait pas être trop grande), ce qui est beaucoup plus rapide que le "order by random ()". lorsque vous avez un grand nombre de lignes, même si elles présentent quelques inconvénients.

Si vos lignes sont plutôt compressées (c.-à-d. Quelques suppressions), vous pouvez alors procéder comme suit (utiliser (select max(rowid) from foo)+1 Au lieu de max(rowid)+1 donne de meilleures performances, comme expliqué dans les commentaires):

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

Si vous avez des trous, vous essayerez parfois de sélectionner un rowid non existant, et la sélection retournera un jeu de résultats vide. Si cela n'est pas acceptable, vous pouvez fournir une valeur par défaut comme celle-ci:

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

Cette deuxième solution n’est pas parfaite: la distribution de probabilité est plus élevée sur la dernière ligne (celle qui contient le plus grand ID de ligne), mais si vous ajoutez souvent des éléments à la table, cela deviendra une cible mouvante et la distribution des probabilités devrait être la même. beaucoup mieux.

Encore une autre solution, si vous sélectionnez souvent des éléments aléatoires dans une table comportant de nombreux trous, vous pouvez créer un tableau contenant les lignes de la table d'origine, triées dans un ordre aléatoire:

create table random_foo(foo_id);

Puis, périodiquement, remplissez à nouveau la table random_foo

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

Et pour sélectionner une ligne aléatoire, 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 quelques problèmes de simultanéité, mais la reconstruction de random_foo est une opération de maintenance qui risque de ne pas se produire très souvent.

Pourtant, une autre façon, récemment trouvée sur une liste de diffusion , est de mettre un déclencheur à la suppression pour déplacer la ligne avec le plus grand ID de ligne dans la ligne actuellement supprimée, de manière à ce qu'il ne reste aucun trou.

Enfin, notez que le comportement de rowid et un incrémentation automatique de la clé primaire n’est pas identique (avec rowid, lorsqu’une nouvelle ligne est insérée, max (rowid) +1 est choisi, où il est le plus élevé jamais vu + 1 pour une clé primaire), donc la dernière solution ne fonctionnera pas avec une auto-incrémentation dans random_foo, mais les autres méthodes le seront.

29
Georges Dupéron

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.

16
Andres Kievsky

Vous devez mettre "order by RANDOM ()" sur votre requête.

Exemple:

select * from quest order by RANDOM();

Voyons un exemple complet

  1. Créer une table:
CREATE TABLE  quest  (
    id  INTEGER PRIMARY KEY AUTOINCREMENT,
    quest TEXT NOT NULL,
    resp_id INTEGER NOT NULL
);

Insérer des valeurs:

insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);

Une sélection par défaut:

select * from quest;

| id |   quest  | resp_id |
   1     1024/4       6
   2     256/2       12
   3     128/1       24
--

Une sélection aléatoire:

select * from quest order by RANDOM();
| id |   quest  | resp_id |
   3     128/1       24
   1     1024/4       6
   2     256/2       12
--

Si vous voulez renvoyer une seule ligne

select * from quest order by RANDOM() LIMIT 1;
| id |   quest  | resp_id |
   2     256/2       12
--
13
Roberto Góes
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1
10
Svetlozar Angelov

Voici une modification de la solution de @ ank:

SELECT * 
FROM table
LIMIT 1 
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)

Cette solution fonctionne également pour les index avec des espaces, car nous avons randomisé un décalage dans une plage [0, count). MAX est utilisé pour gérer un cas avec une table vide.

Voici des résultats de test simples sur une table de 16 000 lignes:

sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117

sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103

sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208
7
vokilam

Je suis venu avec la solution suivante pour le grandes bases de données sqlite:

SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1; 

La fonction abs (X) renvoie la valeur absolue de l'argument numérique X.

La fonction random () renvoie un entier pseudo-aléatoire compris entre -9223372036854775808 et +9223372036854775807.

L'opérateur% renvoie la valeur entière de son opérande gauche modulo de son opérande droit.

Enfin, vous ajoutez +1 pour empêcher rowid égal à 0.

4
Brut