ฉันมีsqlite
ตารางที่มีสคีมาต่อไปนี้:
CREATE TABLE foo (bar VARCHAR)
ฉันใช้ตารางนี้เป็นที่จัดเก็บรายการสตริง
ฉันจะเลือกแถวสุ่มจากตารางนี้ได้อย่างไร?
ฉันมีsqlite
ตารางที่มีสคีมาต่อไปนี้:
CREATE TABLE foo (bar VARCHAR)
ฉันใช้ตารางนี้เป็นที่จัดเก็บรายการสตริง
ฉันจะเลือกแถวสุ่มจากตารางนี้ได้อย่างไร?
คำตอบ:
ดูที่การเลือกแถวสุ่มจากตาราง 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;
ฉันจะได้แถวเดียวกันเสมอ
วิธีแก้ปัญหาต่อไปนี้เร็วกว่าของ anktastic (จำนวน (*) มาก แต่ถ้าคุณแคชได้ความแตกต่างก็ไม่ควรใหญ่ขนาดนั้น) ซึ่งเร็วกว่า "order by random ()" มาก เมื่อคุณมีแถวจำนวนมากแม้ว่าจะมีความไม่สม่ำเสมอเล็กน้อย
หาก rowids ของคุณค่อนข้างแน่น (เช่นการลบเพียงเล็กน้อย) คุณสามารถทำสิ่งต่อไปนี้ได้ (โดยใช้(select max(rowid) from foo)+1
แทนที่จะmax(rowid)+1
ให้ประสิทธิภาพที่ดีขึ้นตามที่อธิบายไว้ในความคิดเห็น):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
หากคุณมีรูบางครั้งคุณจะพยายามเลือก rowid ที่ไม่มีอยู่และการเลือกจะส่งคืนชุดผลลัพธ์ที่ว่างเปล่า หากไม่สามารถยอมรับได้คุณสามารถระบุค่าเริ่มต้นได้ดังนี้:
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;
โซลูชันที่สองนี้ไม่สมบูรณ์แบบ: การกระจายของความน่าจะเป็นจะสูงกว่าในแถวสุดท้าย (อันที่มี rowid สูงสุด) แต่ถ้าคุณใส่ข้อมูลลงในตารางบ่อยๆมันจะกลายเป็นเป้าหมายเคลื่อนที่และการกระจายของความน่าจะเป็นควรเป็น ดีกว่ามาก.
วิธีแก้ปัญหาอีกวิธีหนึ่งหากคุณมักจะเลือกสิ่งที่สุ่มจากตารางที่มีรูจำนวนมากคุณอาจต้องการสร้างตารางที่มีแถวของตารางเดิมที่เรียงลำดับแบบสุ่ม:
create table random_foo(foo_id);
จากนั้นคาบเวลาเติมตาราง random_foo อีกครั้ง
delete from random_foo;
insert into random_foo select id from foo;
และในการเลือกแถวสุ่มคุณสามารถใช้วิธีแรกของฉัน (ไม่มีรูที่นี่) แน่นอนว่าวิธีสุดท้ายนี้มีปัญหาการเกิดขึ้นพร้อมกัน แต่การสร้าง random_foo ขึ้นใหม่เป็นการดำเนินการบำรุงรักษาที่ไม่น่าจะเกิดขึ้นบ่อยนัก
แต่อีกวิธีหนึ่งที่ฉันเพิ่งพบในรายชื่ออีเมลคือการใส่ทริกเกอร์ในการลบเพื่อย้ายแถวที่มี rowid ใหญ่ที่สุดไปยังแถวที่ถูกลบในปัจจุบันเพื่อไม่ให้เหลือช่องว่าง
สุดท้ายโปรดทราบว่าลักษณะการทำงานของ rowid และการเพิ่มอัตโนมัติของคีย์หลักจำนวนเต็มไม่เหมือนกัน (ด้วย rowid เมื่อมีการแทรกแถวใหม่จะมีการเลือก max (rowid) +1 ในขณะที่ค่านั้นเป็นค่าที่สูงที่สุดเท่าที่เคยเห็นมา + 1 สำหรับ คีย์หลัก) ดังนั้นโซลูชันสุดท้ายจะไม่ทำงานกับการเพิ่มอัตโนมัติใน random_foo แต่วิธีอื่น ๆ จะ
SELECT max(rowid) + 1
จะเป็นการค้นหาที่ช้า - ต้องมีการสแกนแบบเต็มตาราง SQLite SELECT max(rowid)
เพียงเพิ่มประสิทธิภาพการค้นหา ดังนั้นคำตอบนี้จะได้รับการปรับปรุงโดย: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
ดูข้อมูลเพิ่มเติมได้ที่sqlite.1065341.n5.nabble.com/…
คุณต้องใส่"order by RANDOM ()"ในข้อความค้นหาของคุณ
ตัวอย่าง:
select * from quest order by RANDOM();
มาดูตัวอย่างที่สมบูรณ์
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
การแทรกค่าบางอย่าง:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
เลือกเริ่มต้น:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
เลือกแบบสุ่ม:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* แต่ละครั้งที่คุณเลือกลำดับจะแตกต่างกัน
หากคุณต้องการกลับเพียงแถวเดียว
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* แต่ละครั้งที่คุณเลือกผลตอบแทนจะแตกต่างกัน
สิ่งที่เกี่ยวกับ:
SELECT COUNT(*) AS n FROM foo;
จากนั้นเลือกตัวเลขสุ่มmใน [0, n) และ
SELECT * FROM foo LIMIT 1 OFFSET m;
คุณสามารถบันทึกหมายเลขแรก ( n ) ไว้ที่ไหนสักแห่งและอัปเดตเมื่อจำนวนฐานข้อมูลเปลี่ยนแปลงเท่านั้น ด้วยวิธีนี้คุณไม่จำเป็นต้องทำการ SELECT COUNT ทุกครั้ง
OFFSET
ดูเหมือนว่าจะเพิ่มขึ้นตามขนาดของออฟเซ็ต - แถวที่ 2 นั้นเร็วแถว 2 ล้านจะใช้เวลาสักครู่แม้ว่าข้อมูลทั้งหมดจะเป็นขนาดคงที่และก็ตาม ควรจะสามารถค้นหาได้โดยตรง อย่างน้อยนั่นคือสิ่งที่ดูเหมือนใน SQLite 3.7.13
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
นี่คือการปรับเปลี่ยนโซลูชันของ @ ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
โซลูชันนี้ใช้ได้กับดัชนีที่มีช่องว่างเช่นกันเนื่องจากเราสุ่มค่าชดเชยในช่วง [0, count) MAX
ใช้สำหรับจัดการเคสที่มีโต๊ะว่าง
นี่คือผลการทดสอบอย่างง่ายบนตารางที่มีแถว 16k:
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
ฉันคิดวิธีแก้ปัญหาต่อไปนี้สำหรับฐานข้อมูล sqlite3 ขนาดใหญ่ :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
ฟังก์ชัน abs (X) ส่งกลับค่าสัมบูรณ์ของอาร์กิวเมนต์ตัวเลข X
ฟังก์ชัน random () ส่งคืนจำนวนเต็มสุ่มหลอกระหว่าง -9223372036854775808 และ +9223372036854775807
% ตัวดำเนินการจะส่งออกค่าจำนวนเต็มของตัวถูกดำเนินการด้านซ้ายของโมดูโลตัวถูกดำเนินการด้านขวา
สุดท้ายคุณเพิ่ม +1 เพื่อป้องกันไม่ให้ rowid เท่ากับ 0