Bagaimana cara mengambil sampel acak sederhana yang efisien dalam SQL? Database yang dimaksud menjalankan MySQL; tabel saya setidaknya 200.000 baris, dan saya ingin sampel acak sederhana sekitar 10.000.
Jawaban yang "jelas" adalah:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Untuk tabel besar, itu terlalu lambat: ia memanggil RAND()
setiap baris (yang sudah menempatkannya di O (n)), dan mengurutkannya, menjadikannya O (n lg n) paling baik. Apakah ada cara untuk melakukan ini lebih cepat dari O (n)?
Catatan : Seperti yang ditunjukkan Andrew Mao di komentar, Jika Anda menggunakan pendekatan ini di SQL Server, Anda harus menggunakan fungsi T-SQL NEWID()
, karena RAND () dapat mengembalikan nilai yang sama untuk semua baris .
EDIT: 5 TAHUN KEMUDIAN
Saya mengalami masalah ini lagi dengan tabel yang lebih besar, dan akhirnya menggunakan versi solusi @ ignorant, dengan dua penyesuaian:
- Sampel baris menjadi 2-5x ukuran sampel yang saya inginkan, dengan harga murah
ORDER BY RAND()
- Simpan hasil
RAND()
ke kolom terindeks di setiap penyisipan / pembaruan. (Jika kumpulan data Anda tidak terlalu banyak memperbarui, Anda mungkin perlu menemukan cara lain untuk menjaga kolom ini tetap segar.)
Untuk mengambil sampel 1000 item dari sebuah tabel, saya menghitung baris dan mengambil sampel hasilnya hingga, rata-rata, 10.000 baris dengan kolom frozen_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
(Implementasi aktual saya melibatkan lebih banyak pekerjaan untuk memastikan saya tidak kekurangan sampel, dan untuk membungkus secara manual rand_high, tetapi ide dasarnya adalah "secara acak potong N Anda menjadi beberapa ribu.")
Meskipun ini membuat beberapa pengorbanan, ini memungkinkan saya untuk mengambil sampel database menggunakan pemindaian indeks, hingga cukup kecil untuk digunakan ORDER BY RAND()
kembali.
RAND()
mengembalikan nilai yang sama setiap panggilan berikutnya.