sqlite
Aşağıdaki şemaya sahip bir tablom var:
CREATE TABLE foo (bar VARCHAR)
Bu tabloyu dizelerin listesi için depo olarak kullanıyorum.
Bu tablodan rastgele bir satırı nasıl seçerim?
sqlite
Aşağıdaki şemaya sahip bir tablom var:
CREATE TABLE foo (bar VARCHAR)
Bu tabloyu dizelerin listesi için depo olarak kullanıyorum.
Bu tablodan rastgele bir satırı nasıl seçerim?
Yanıtlar:
Bir SQLite Tablosundan Rastgele Bir Satır Seçmeye bir göz atın
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;
hep aynı satırı alıyorum.
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)+1
yerine 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.
SELECT max(rowid) + 1
yavaş 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/…
Sorgunuza "RASGELE SIRASI ()" koymanız gerekir .
Misal:
select * from quest order by RANDOM();
Tam bir örnek görelim
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.
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.
OFFSET
de, 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.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
İş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. MAX
boş 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
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.