Làm cách nào để lấy một mẫu ngẫu nhiên đơn giản hiệu quả trong SQL? Cơ sở dữ liệu được đề cập đang chạy MySQL; bảng của tôi có ít nhất 200.000 hàng và tôi muốn một mẫu ngẫu nhiên đơn giản khoảng 10.000.
Câu trả lời "hiển nhiên" là:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Đối với các bảng lớn, điều đó quá chậm: nó gọi RAND()
mọi hàng (đã đặt nó ở vị trí O (n)) và sắp xếp chúng, làm cho nó tốt nhất là O (n lg n). Có cách nào để làm điều này nhanh hơn O (n) không?
Lưu ý : Như Andrew Mao đã chỉ ra trong phần nhận xét, Nếu bạn đang sử dụng phương pháp này trên SQL Server, bạn nên sử dụng hàm T-SQL NEWID()
, vì RAND () có thể trả về cùng một giá trị cho tất cả các hàng .
CHỈNH SỬA: 5 NĂM SAU
Tôi lại gặp phải vấn đề này với một bảng lớn hơn và kết thúc bằng cách sử dụng phiên bản giải pháp của @ ignore, với hai tinh chỉnh:
- Lấy mẫu các hàng lên gấp 2-5 lần kích thước mẫu mong muốn của tôi, với giá rẻ
ORDER BY RAND()
- Lưu kết quả
RAND()
vào một cột được lập chỉ mục trên mỗi lần chèn / cập nhật. (Nếu tập dữ liệu của bạn không quá cập nhật, bạn có thể cần phải tìm một cách khác để giữ cho cột này luôn mới.)
Để lấy mẫu bảng gồm 1000 mục, tôi đếm các hàng và lấy mẫu kết quả trung bình xuống, trung bình là 10.000 hàng với cột freeze_rand:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(Việc triển khai thực tế của tôi liên quan đến nhiều công việc hơn để đảm bảo tôi không lấy mẫu và quấn rand_high theo cách thủ công, nhưng ý tưởng cơ bản là "cắt ngẫu nhiên N của bạn xuống một vài nghìn".)
Mặc dù điều này gây ra một số hy sinh, nhưng nó cho phép tôi lấy mẫu cơ sở dữ liệu bằng cách sử dụng quét chỉ mục, cho đến khi nó đủ nhỏ để làm ORDER BY RAND()
lại.
RAND()
trả về cùng một giá trị cho mỗi lần gọi tiếp theo.