Sqlite tablodan rastgele satır seçin


Yanıtlar:


213

Bir SQLite Tablosundan Rastgele Bir Satır Seçmeye bir göz atın

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

1
Bu çözümü bir birleşime nasıl genişletebilirim? Kullanırken SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;hep aynı satırı alıyorum.
Helmut Grohne

Rastgele sayıyı tohumlamak mümkün mü? Örneğin, günün kitabı bugün öğlen için unix epoc ile ekildi, böylece sorgu birden çok kez çalıştırılsa bile tüm gün aynı kitabı gösterir. Evet, bu kullanım örneği için önbelleğe almanın daha verimli olduğunu biliyorum, sadece bir örnek.
danielson317

FWIW sorum aslında burada cevaplandı. Ve cevap şu ki, rastgele sayıyı tohumlayamazsınız. stackoverflow.com/questions/24256258/…
danielson317

31

Aşağıdaki çözümler anktastic'ten çok daha hızlıdır (count (*) çok pahalıdır, ancak önbelleğe alabilirseniz, fark o kadar büyük olmamalıdır), ki bu da "rastgele sırala ()" dan çok daha hızlıdır. birkaç sakıncası olmasına rağmen çok sayıda satırınız olduğunda.

Eğer kürekleriniz oldukça doluysa (yani, birkaç silme işlemi), o zaman aşağıdakileri yapabilirsiniz ( yorumlarda açıklandığı gibi daha iyi performans sağlamak (select max(rowid) from foo)+1yerine kullanmak max(rowid)+1):

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

Delikleriniz varsa, bazen var olmayan bir satır kimliği seçmeye çalışırsınız ve seçim boş bir sonuç kümesi döndürür. Bu kabul edilebilir değilse, bunun gibi bir varsayılan değer sağlayabilirsiniz:

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;

Bu ikinci çözüm mükemmel değildir: Olasılık dağılımı son satırda daha yüksektir (en yüksek satır kimliğine sahip olan), ancak tabloya sık sık malzeme eklerseniz, bu hareketli bir hedef haline gelir ve olasılıkların dağılımı çok daha iyi.

Yine başka bir çözüm, çok sayıda deliği olan bir tablodan sık sık rastgele şeyler seçerseniz, orijinal tablonun rasgele sırayla sıralanmış satırlarını içeren bir tablo oluşturmak isteyebilirsiniz:

create table random_foo(foo_id);

Ardından, periyodik olarak, random_foo tablosunu yeniden doldurun

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

Ve rastgele bir sıra seçmek için ilk yöntemimi kullanabilirsiniz (burada delik yok). Elbette, bu son yöntemin bazı eşzamanlılık sorunları vardır, ancak random_foo'nun yeniden oluşturulması pek sık gerçekleşmesi muhtemel olmayan bir bakım işlemidir.

Yine de, son zamanlarda bir posta listesinde bulduğum başka bir yol da, en büyük satır kimliğine sahip satırı geçerli silinen satıra taşımak için silme işlemini tetiklemek, böylece hiçbir delik kalmamasıdır.

Son olarak, rowid ve bir tamsayı birincil anahtar otomatik artışının davranışının aynı olmadığını unutmayın (rowid ile, yeni bir satır eklendiğinde, max (rowid) +1 seçilir, ancak şimdiye kadar görülen en yüksek değer + 1'dir. birincil anahtar), bu nedenle son çözüm random_foo'da otomatik artışla çalışmaz, ancak diğer yöntemler çalışır.


Yerine (yöntem 2) geri dönüş yöntem sahip bir posta listesinde sadece testere, gibi, kullandığınız sadece edebilirsiniz rowid> = [rastgele] yerine of =, ama aslında slugissingly yavaş yönteme 2. karşılaştırılır
Suzanne Dupéron

3
Bu harika bir cevap; ancak bir sorunu var. SELECT max(rowid) + 1yavaş bir sorgu olacaktır - tam bir tablo taraması gerektirir. sqlite yalnızca sorguyu optimize eder SELECT max(rowid). Böylece, bu cevap şu şekilde geliştirilebilir: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)); Daha fazla bilgi için buna bakın: sqlite.1065341.n5.nabble.com/…
dasl

19

Sorgunuza "RASGELE SIRASI ()" koymanız gerekir .

Misal:

select * from quest order by RANDOM();

Tam bir örnek görelim

  1. Bir tablo oluşturun:
CREATE TABLE  quest  (
    id  INTEGER PRIMARY KEY AUTOINCREMENT,
    quest TEXT NOT NULL,
    resp_id INTEGER NOT NULL
);

Bazı değerler eklemek:

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

Varsayılan bir seçim:

select * from quest;

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

Rastgele bir seçim:

select * from quest order by RANDOM();
| id |   quest  | resp_id |
   3     128/1       24
   1     1024/4       6
   2     256/2       12
--
* Seçtiğiniz her seferinde sıra farklı olacaktır.

Yalnızca bir satır döndürmek istiyorsanız

select * from quest order by RANDOM() LIMIT 1;
| id |   quest  | resp_id |
   2     256/2       12
--
* Seçtiğiniz her seferinde dönüş farklı olacaktır.


Yalnızca kod yanıtları yasak olmasa da, lütfen bunun kitle kaynaklı bir topluluktan ziyade bir Soru-Cevap topluluğu olduğunu ve OP, gönderilen kodu bir yanıt olarak anlarsa, ortaya çıkacağını anlayın. kendi başına benzer bir çözümle ve her şeyden önce bir soru sormazdı. Bu nedenle, lütfen nasıl ve / veya neden çalıştığını açıklayarak cevabınıza ve / veya kodunuza bağlam sağlayın .
XenoRo

2
Bu çözümü tercih ediyorum çünkü n satırı aramama izin veriyor. Benim durumumda, veritabanından 100 rastgele örneğe ihtiyacım vardı - ORDER BY RANDOM () ile birlikte LIMIT 100 tam olarak bunu yapıyor.
mnr

17

Ne dersin:

SELECT COUNT(*) AS n FROM foo;

sonra [0, n) 'de rastgele bir m sayısı seçin ve

SELECT * FROM foo LIMIT 1 OFFSET m;

İlk sayıyı ( n ) bir yere kaydedebilir ve yalnızca veritabanı sayısı değiştiğinde güncelleyebilirsiniz. Bu şekilde her seferinde SEÇİM SAYISI yapmanız gerekmez.


1
Bu güzel ve hızlı bir yöntem. 1'den fazla satır seçmek pek iyi bir şekilde genellemez, ancak OP yalnızca 1'i istedi, bu yüzden sanırım sorun değil.
Ken Williams

Unutulmaması gereken ilginç bir şey OFFSETde, ofsetin boyutuna bağlı olarak görüneni bulmak için gereken sürenin artmasıdır - 2. satır hızlıdır, 2. satırdaki tüm veriler sabit boyutta olsa bile biraz zaman alır ve doğrudan onu arayabilmeli. En azından SQLite 3.7.13'te göründüğü gibi.
Ken Williams

@KenWilliams Hemen hemen tüm veritabanlarında `` OFFSET '' ile aynı sorun var. O kadar fazla satırı okumak gerekiyor çünkü sadece 1. dönecektir rağmen bir veritabanını sorgulamak için çok verimsiz bir yoludur
Jonathan Allen

1
Not Konuşuyordum bu konuda / sabit boyutu / gerçi kayıtları - bu verilerde doğru byte (doğrudan tarama kolay olmalı değil okuma sayıda satır), ama bunlar açıkça optimizasyonu uygulamak gerekir.
Ken Williams

@KenWilliams: SQLite'de sabit boyutlu kayıtlar yoktur, dinamik olarak yazılmıştır ve verilerin beyan edilen benzerliklerle eşleşmesi gerekmez ( sqlite.org/fileformat2.html#section_2_1 ). Her şey b-ağacı sayfalarında saklanır, yani her iki durumda da yaprağa doğru en azından bir b-ağacı araması yapmak zorundadır. Bunu verimli bir şekilde başarmak için, alt ağacın boyutunu her çocuk göstericiyle birlikte saklaması gerekir.
Birleştirme

13
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1

11
Önce tüm tablo içeriğini seçeceğinden, bu büyük tablolar için çok zaman alıcı olmaz mı?
Alex_coder

1
"WHERE" koşullarını kullanarak kapsamı sınırlayamaz mısınız?
jldupont

11

İşte @ ank çözümünün bir değişikliği:

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

Bu çözüm aynı zamanda boşluklu indisler için de işe yarar, çünkü [0, sayım) aralığında bir ofseti rastgele seçiyoruz. MAXboş tablo olan bir durumu işlemek için kullanılır.

16k satırlı bir tabloda basit test sonuçları şunlardır:

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

4

Büyük sqlite3 veritabanları için aşağıdaki çözümü buldum :

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

Abs (X) işlevi, sayısal değişken X'in mutlak değerini döndürür.

Random () işlevi, -9223372036854775808 ile +9223372036854775807 arasında sözde rastgele bir tamsayı döndürür.

% Operatörü, sol işlenen modulunun tamsayı değerini, sağ işlenenini verir.

Son olarak, rowid'in 0'a eşit olmasını önlemek için +1 eklersiniz.


1
İyi deneme ama bunun işe yarayacağını sanmıyorum. Ya rowId = 5 olan bir satır silindiyse, ancak 1,2,3,4,6,7,8,9,10 satır kimlikleri hala mevcutsa? Ardından, seçilen rastgele rowId 5 ise, bu sorgu hiçbir şey döndürmez.
Calicoder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.