Tôi có một sqlite
bảng với lược đồ sau:
CREATE TABLE foo (bar VARCHAR)
Tôi đang sử dụng bảng này làm nơi lưu trữ danh sách các chuỗi.
Làm cách nào để chọn một hàng ngẫu nhiên từ bảng này?
Tôi có một sqlite
bảng với lược đồ sau:
CREATE TABLE foo (bar VARCHAR)
Tôi đang sử dụng bảng này làm nơi lưu trữ danh sách các chuỗi.
Làm cách nào để chọn một hàng ngẫu nhiên từ bảng này?
Câu trả lời:
Xem phần Chọn hàng ngẫu nhiên từ bảng 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;
tôi luôn nhận được cùng một hàng.
Các giải pháp sau đây nhanh hơn nhiều so với anktastic (số lượng (*) tốn rất nhiều, nhưng nếu bạn có thể lưu vào bộ nhớ cache, thì sự khác biệt sẽ không quá lớn), bản thân nó nhanh hơn nhiều so với "order by random ()" khi bạn có một số lượng lớn các hàng, mặc dù chúng có một vài điểm bất tiện.
Nếu các rowid của bạn khá được đóng gói (tức là ít lần xóa), thì bạn có thể làm như sau (sử dụng (select max(rowid) from foo)+1
thay vì max(rowid)+1
mang lại hiệu suất tốt hơn, như được giải thích trong phần nhận xét):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Nếu bạn có lỗ hổng, đôi khi bạn sẽ cố gắng chọn một rowid không tồn tại và lựa chọn sẽ trả về một tập hợp kết quả trống. Nếu điều này không được chấp nhận, bạn có thể cung cấp giá trị mặc định như sau:
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;
Giải pháp thứ hai này không hoàn hảo: phân phối xác suất cao hơn trên hàng cuối cùng (hàng có hàng cao nhất), nhưng nếu bạn thường xuyên thêm nội dung vào bảng, nó sẽ trở thành mục tiêu di động và phân phối xác suất sẽ tốt hơn nhiều.
Tuy nhiên, một giải pháp khác, nếu bạn thường chọn nội dung ngẫu nhiên từ một bảng có nhiều lỗ, thì bạn có thể muốn tạo một bảng chứa các hàng của bảng ban đầu được sắp xếp theo thứ tự ngẫu nhiên:
create table random_foo(foo_id);
Sau đó, định kỳ, điền lại bảng random_foo
delete from random_foo;
insert into random_foo select id from foo;
Và để chọn một hàng ngẫu nhiên, bạn có thể sử dụng phương pháp đầu tiên của tôi (không có lỗ nào ở đây). Tất nhiên, phương pháp cuối cùng này có một số vấn đề về đồng thời, nhưng việc xây dựng lại random_foo là một hoạt động bảo trì không có khả năng xảy ra thường xuyên.
Tuy nhiên, một cách khác, mà tôi đã tìm thấy gần đây trên danh sách gửi thư , là đặt kích hoạt xóa để di chuyển hàng có rowid lớn nhất vào hàng đã xóa hiện tại, để không còn lỗ nào.
Cuối cùng, lưu ý rằng hành vi của rowid và tự động gia tăng khóa chính của một số nguyên không giống nhau (với rowid, khi một hàng mới được chèn, max (rowid) +1 được chọn, khi đó nó là cao nhất-giá trị-từng thấy + 1 cho khóa chính), vì vậy giải pháp cuối cùng sẽ không hoạt động với autoincrement trong random_foo, nhưng các phương thức khác sẽ làm được.
SELECT max(rowid) + 1
sẽ là một truy vấn chậm - nó yêu cầu quét toàn bộ bảng. sqlite chỉ tối ưu hóa truy vấn SELECT max(rowid)
. Do đó, câu trả lời này sẽ được cải thiện bằng cách: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Xem phần này để biết thêm thông tin: sqlite.1065341.n5.nabble.com/…
Bạn cần đặt "order by RANDOM ()" vào truy vấn của mình.
Thí dụ:
select * from quest order by RANDOM();
Hãy xem một ví dụ hoàn chỉnh
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
Chèn một số giá trị:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
Một lựa chọn mặc định:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
Một lựa chọn ngẫu nhiên:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* Mỗi lần bạn chọn, thứ tự sẽ khác nhau.
Nếu bạn chỉ muốn trả lại một hàng
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* Mỗi lần bạn chọn, kết quả trả về sẽ khác nhau.
Thế còn:
SELECT COUNT(*) AS n FROM foo;
sau đó chọn một số ngẫu nhiên m trong [0, n) và
SELECT * FROM foo LIMIT 1 OFFSET m;
Bạn thậm chí có thể lưu số đầu tiên ( n ) ở đâu đó và chỉ cập nhật nó khi số lượng cơ sở dữ liệu thay đổi. Bằng cách đó, bạn không phải thực hiện SELECT COUNT mỗi lần.
OFFSET
dường như tăng lên tùy thuộc vào kích thước của khoảng chênh lệch - hàng 2 là nhanh, hàng 2 triệu mất một lúc, ngay cả khi tất cả dữ liệu trong đó là kích thước cố định và nên có thể trực tiếp tìm kiếm nó. Ít nhất, đó là những gì nó trông giống như trong SQLite 3.7.13.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
Đây là một sửa đổi của giải pháp @ ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
Giải pháp này cũng hoạt động đối với các chỉ số có khoảng trống, bởi vì chúng tôi ngẫu nhiên hóa một phần bù trong phạm vi [0, đếm). MAX
được sử dụng để xử lý một trường hợp có bảng trống.
Dưới đây là kết quả kiểm tra đơn giản trên bảng có 16k hàng:
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
Tôi đã đưa ra giải pháp sau cho cơ sở dữ liệu sqlite3 lớn :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
Hàm abs (X) trả về giá trị tuyệt đối của đối số số X.
Hàm random () trả về một số nguyên giả ngẫu nhiên giữa -9223372036854775808 và +9223372036854775807.
Toán tử% xuất ra giá trị nguyên của modul toán hạng bên trái cho toán hạng bên phải của nó.
Cuối cùng, bạn thêm +1 để ngăn rowid bằng 0.