TABLESAMPLE
ฉันได้มาด้วยวิธีการแก้ปัญหาอย่างรวดเร็วโดยไม่ต้อง เร็วกว่าOFFSET random()*N LIMIT 1
. ไม่จำเป็นต้องมีการนับตาราง
ความคิดที่จะสร้างดัชนีการแสดงออกที่มีข้อมูลแบบสุ่ม md5(primary key)
แต่คาดเดาได้เช่น
นี่คือการทดสอบด้วยข้อมูลตัวอย่าง 1M แถว:
create table randtest (id serial primary key, data int not null);
insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000);
create index randtest_md5_id_idx on randtest (md5(id::text));
explain analyze
select * from randtest where md5(id::text)>md5(random()::text)
order by md5(id::text) limit 1;
ผลลัพธ์:
Limit (cost=0.42..0.68 rows=1 width=8) (actual time=6.219..6.220 rows=1 loops=1)
-> Index Scan using randtest_md5_id_idx on randtest (cost=0.42..84040.42 rows=333333 width=8) (actual time=6.217..6.217 rows=1 loops=1)
Filter: (md5((id)::text) > md5((random())::text))
Rows Removed by Filter: 1831
Total runtime: 6.245 ms
บางครั้งการสืบค้นนี้อาจ (มีความน่าจะเป็นประมาณ 1 / Number_of_rows) ส่งคืน 0 แถวดังนั้นจึงจำเป็นต้องตรวจสอบและเรียกใช้ใหม่ ความน่าจะเป็นก็ไม่เหมือนกันทุกประการ - บางแถวมีความเป็นไปได้มากกว่าแถวอื่น ๆ
สำหรับการเปรียบเทียบ:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
ผลลัพธ์แตกต่างกันไปมาก แต่ก็ค่อนข้างแย่:
Limit (cost=1442.50..1442.51 rows=1 width=4) (actual time=179.183..179.184 rows=1 loops=1)
-> Seq Scan on randtest (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.016..134.835 rows=915702 loops=1)
Total runtime: 179.211 ms
(3 rows)