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?
Jetez un oeil à Sélection d'une ligne aléatoire à partir d'une table SQLite
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
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.
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.
Vous devez mettre "order by RANDOM ()" sur votre requête.
Exemple:
select * from quest order by RANDOM();
Voyons un exemple complet
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
--
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
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
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.