Tenho uma sqlite
tabela com o seguinte esquema:
CREATE TABLE foo (bar VARCHAR)
Estou usando esta tabela como armazenamento para uma lista de strings.
Como seleciono uma linha aleatória desta tabela?
Tenho uma sqlite
tabela com o seguinte esquema:
CREATE TABLE foo (bar VARCHAR)
Estou usando esta tabela como armazenamento para uma lista de strings.
Como seleciono uma linha aleatória desta tabela?
Respostas:
Dê uma olhada em Selecionando uma linha aleatória de uma tabela SQLite
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
, sempre obtenho a mesma linha.
As soluções a seguir são muito mais rápidas do que as da anktastic (a contagem (*) custa muito, mas se você pode armazená-la em cache, a diferença não deve ser tão grande), que por si só é muito mais rápida do que "ordenar por acaso ()" quando você tem um grande número de linhas, embora tenham alguns inconvenientes.
Se seus rowids estiverem bastante compactados (ou seja, poucas exclusões), você pode fazer o seguinte (usar em (select max(rowid) from foo)+1
vez de max(rowid)+1
oferece melhor desempenho, conforme explicado nos comentários):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Se você tiver buracos, às vezes tentará selecionar um rowid inexistente e o select retornará um conjunto de resultados vazio. Se isso não for aceitável, você pode fornecer um valor padrão como este:
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;
Esta segunda solução não é perfeita: a distribuição de probabilidade é mais alta na última linha (aquela com o rowid mais alto), mas se você adicionar coisas frequentemente à tabela, ela se tornará um alvo móvel e a distribuição de probabilidades deve ser muito melhor.
Ainda outra solução, se você costuma selecionar coisas aleatórias de uma mesa com muitos buracos, então você pode querer criar uma tabela que contém as linhas da tabela original classificadas em ordem aleatória:
create table random_foo(foo_id);
Então, periodicamente, preencha novamente a tabela random_foo
delete from random_foo;
insert into random_foo select id from foo;
E para selecionar uma linha aleatória, você pode usar meu primeiro método (não há buracos aqui). Claro, este último método tem alguns problemas de simultaneidade, mas a reconstrução de random_foo é uma operação de manutenção que provavelmente não acontecerá com frequência.
Ainda, outra maneira, que encontrei recentemente em uma lista de e-mails , é colocar um gatilho em delete para mover a linha com o maior rowid para a linha excluída atual, de modo que nenhum buraco seja deixado.
Por último, observe que o comportamento de rowid e um incremento automático de chave primária inteira não é idêntico (com rowid, quando uma nova linha é inserida, max (rowid) +1 é escolhido, onde é o valor mais alto já visto + 1 para uma chave primária), então a última solução não funcionará com um incremento automático em random_foo, mas os outros métodos sim.
SELECT max(rowid) + 1
será uma consulta lenta - requer uma verificação completa da tabela. sqlite apenas otimiza a consulta SELECT max(rowid)
. Assim, esta resposta seria melhorada por: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Veja isto para mais informações: sqlite.1065341.n5.nabble.com/…
Você precisa colocar "ordem por RANDOM ()" em sua consulta.
Exemplo:
select * from quest order by RANDOM();
Vamos ver um exemplo completo
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
Inserindo alguns valores:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
Uma seleção padrão:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
Uma seleção aleatória:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* Cada vez que você selecionar, o pedido será diferente.
Se você quiser retornar apenas uma linha
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* Cada vez que você selecionar, o retorno será diferente.
A respeito:
SELECT COUNT(*) AS n FROM foo;
em seguida, escolha um número aleatório m em [0, n) e
SELECT * FROM foo LIMIT 1 OFFSET m;
Você pode até salvar o primeiro número ( n ) em algum lugar e apenas atualizá-lo quando a contagem do banco de dados mudar. Dessa forma, você não precisa fazer SELECT COUNT todas as vezes.
OFFSET
parece aumentar dependendo do tamanho do deslocamento - a linha 2 é rápida, a linha 2 milhões demora um pouco, mesmo quando todos os dados no são de tamanho fixo e deve ser capaz de procurá-lo diretamente. Pelo menos, é o que parece no SQLite 3.7.13.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
Aqui está uma modificação da solução de @ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
Essa solução também funciona para índices com lacunas, porque randomizamos um deslocamento em um intervalo [0, contagem). MAX
é usado para lidar com um caso com mesa vazia.
Aqui estão resultados de teste simples em uma tabela com 16 mil linhas:
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
Eu vim com a seguinte solução para os grandes bancos de dados sqlite3 :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
A função abs (X) retorna o valor absoluto do argumento numérico X.
A função random () retorna um número inteiro pseudoaleatório entre -9223372036854775808 e +9223372036854775807.
O operador% produz o valor inteiro de seu operando esquerdo módulo de seu operando direito.
Finalmente, você adiciona +1 para evitar que rowid seja igual a 0.