Tôi đã nghĩ ra một giải pháp rất nhanh mà không cần TABLESAMPLE. Nhanh hơn nhiều so với OFFSET random()*N LIMIT 1. Nó thậm chí không yêu cầu đếm bảng.
Ý tưởng là tạo một chỉ mục biểu thức với dữ liệu ngẫu nhiên nhưng có thể dự đoán được, chẳng hạn md5(primary key).
Đây là một thử nghiệm với dữ liệu mẫu 1 triệu hàng:
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;
Kết quả:
 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
Truy vấn này đôi khi có thể (với xác suất khoảng 1 / Number_of_rows) trả về 0 hàng, vì vậy nó cần được kiểm tra và chạy lại. Ngoài ra, các xác suất không hoàn toàn giống nhau - một số hàng có nhiều xác suất hơn những hàng khác.
Để so sánh:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
Kết quả rất khác nhau, nhưng có thể khá tệ:
 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)