Saya memiliki permintaan di mana saya ingin catatan yang dihasilkan dipesan secara acak. Ini menggunakan indeks berkerumun, jadi jika saya tidak menyertakan order by
itu kemungkinan akan mengembalikan catatan dalam urutan indeks itu. Bagaimana saya bisa memastikan urutan baris acak?
Saya mengerti bahwa kemungkinan tidak akan "benar-benar" acak, pseudo-acak cukup baik untuk kebutuhan saya.
sql-server
goric
sumber
sumber
CryptGenRandom
pada akhirnya. dba.stackexchange.com/a/208069/3690Saran pertama Pradeep Adiga
ORDER BY NEWID()
,, baik-baik saja dan sesuatu yang saya gunakan di masa lalu karena alasan ini.Berhati-hatilah dengan menggunakan
RAND()
- dalam banyak konteks hanya dieksekusi sekali per pernyataan sehingga tidakORDER BY RAND()
akan berpengaruh (karena Anda mendapatkan hasil yang sama dari RAND () untuk setiap baris).Contohnya:
mengembalikan setiap nama dari tabel orang kami dan nomor "acak", yang sama untuk setiap baris. Jumlahnya bervariasi setiap kali Anda menjalankan kueri, tetapi sama untuk setiap baris setiap kali.
Untuk menunjukkan bahwa sama halnya dengan
RAND()
yang digunakan dalamORDER BY
klausa, saya mencoba:Hasilnya masih dipesan dengan nama yang menunjukkan bahwa bidang pengurutan sebelumnya (yang diharapkan acak) tidak berpengaruh sehingga mungkin selalu memiliki nilai yang sama.
Memesan dengan
NEWID()
tidak berfungsi, karena jika NEWID () tidak selalu dinilai ulang tujuan UUIDs akan rusak ketika memasukkan banyak baris baru dalam satu statemnt dengan pengidentifikasi unik sebagai kuncinya, jadi:tidak memesan nama-nama "secara acak".
DBMS lainnya
Di atas berlaku untuk MSSQL (setidaknya 2005 dan 2008, dan jika saya ingat juga 2000). Fungsi mengembalikan UUID baru harus dievaluasi setiap kali di semua DBMS NEWID () berada di bawah MSSQL tetapi perlu memverifikasi ini dalam dokumentasi dan / atau dengan tes Anda sendiri. Perilaku fungsi hasil arbitrer lainnya, seperti RAND (), lebih cenderung bervariasi di antara DBMS, jadi sekali lagi periksa dokumentasi.
Saya juga melihat pemesanan dengan nilai-nilai UUID diabaikan dalam beberapa konteks karena DB mengasumsikan bahwa tipe tidak memiliki urutan yang berarti. Jika Anda menemukan ini adalah kasus yang secara eksplisit melemparkan UUID ke tipe string dalam klausa pemesanan, atau membungkus beberapa fungsi lain di sekitarnya seperti
CHECKSUM()
di SQL Server (mungkin ada perbedaan kinerja yang kecil dari ini juga karena pemesanan akan dilakukan pada nilai 32-bit bukan 128-bit, meskipun apakah manfaatnya lebih besar daripada biaya menjalankanCHECKSUM()
per nilai, saya akan meninggalkan Anda untuk menguji).Catatan Samping
Jika Anda menginginkan pemesanan yang sewenang-wenang tetapi agak dapat diulang, pesanlah dengan subset data yang relatif tidak terkontrol di baris itu sendiri. Misalnya salah satu atau ini akan mengembalikan nama dalam urutan yang sewenang-wenang tetapi berulang:
Pemesanan sewenang-wenang tetapi berulang tidak sering berguna dalam aplikasi, meskipun dapat berguna dalam pengujian jika Anda ingin menguji beberapa kode pada hasil dalam berbagai pesanan tetapi ingin dapat mengulangi setiap menjalankan dengan cara yang sama beberapa kali (untuk mendapatkan waktu rata-rata hasil lebih dari beberapa kali berjalan, atau pengujian bahwa perbaikan yang Anda lakukan pada kode tidak menghilangkan masalah atau ketidakefisienan yang sebelumnya disorot oleh inputet hasil tertentu, atau hanya untuk menguji bahwa kode Anda "stabil" yang mengembalikan hasil yang sama setiap kali jika mengirim data yang sama dalam urutan tertentu).
Trik ini juga dapat digunakan untuk mendapatkan hasil yang lebih sewenang-wenang dari fungsi, yang tidak memungkinkan panggilan non-deterministik seperti NEWID () di dalam tubuh mereka. Sekali lagi, ini bukan sesuatu yang mungkin sering berguna di dunia nyata tetapi bisa berguna jika Anda ingin fungsi mengembalikan sesuatu yang acak dan "acak-ish" cukup baik (tapi hati-hati mengingat aturan yang menentukan ketika fungsi yang ditentukan pengguna dievaluasi, yaitu biasanya hanya sekali per baris, atau hasil Anda mungkin tidak seperti yang Anda harapkan / butuhkan).
Performa
Seperti yang ditunjukkan EBarr, mungkin ada masalah kinerja dengan salah satu di atas. Untuk lebih dari beberapa baris Anda hampir dijamin untuk melihat output spooled ke tempdb sebelum jumlah baris yang diminta dibaca kembali dalam urutan yang benar, yang berarti bahwa bahkan jika Anda mencari 10 besar, Anda mungkin menemukan indeks lengkap pemindaian (atau lebih buruk, pemindaian tabel) terjadi bersamaan dengan blok penulisan yang sangat besar ke tempdb. Karenanya sangat penting, seperti halnya kebanyakan hal, untuk melakukan tolok ukur dengan data realistis sebelum menggunakan ini dalam produksi.
sumber
Ini adalah pertanyaan lama, tetapi salah satu aspek dari diskusi tersebut hilang, menurut saya - PERFORMANCE
ORDER BY NewId()
adalah jawaban umum. Ketika mewah seseorang mendapatkan, mereka menambahkan bahwa Anda harus benar-benar membungkusNewID()
diCheckSum()
, Anda tahu, untuk kinerja!Masalah dengan metode ini, adalah Anda masih dijamin pemindaian indeks lengkap dan kemudian semacam data lengkap. Jika Anda pernah bekerja dengan volume data serius apa pun, ini bisa dengan cepat menjadi mahal. Lihatlah rencana eksekusi yang khas ini, dan perhatikan bagaimana prosesnya memakan waktu 96% dari waktu Anda ...
Untuk memberi Anda gambaran bagaimana skala ini, saya akan memberi Anda dua contoh dari database yang saya gunakan.
Order By newid()
di tabel ini menghasilkan 53.700 bacaan dan membutuhkan waktu 16 detik.Moral dari cerita ini adalah bahwa jika Anda memiliki tabel besar (pikirkan miliaran baris) atau perlu menjalankan kueri ini sering
newid()
metode rusak. Jadi apa yang harus dilakukan anak laki-laki?Memenuhi TABLESAMPLE ()
Dalam SQL 2005 kemampuan baru yang disebut
TABLESAMPLE
telah dibuat. Saya hanya melihat satu artikel yang membahas penggunaannya ... seharusnya ada lebih banyak. MSDN Documents di sini . Pertama sebuah contoh:Gagasan di balik sampel tabel adalah memberi Anda kira - kira ukuran subset yang Anda minta. SQL memberi nomor pada setiap halaman data dan memilih X persen dari halaman tersebut. Jumlah aktual baris yang Anda dapat kembali dapat bervariasi berdasarkan pada apa yang ada di halaman yang dipilih.
Jadi bagaimana saya menggunakannya? Pilih ukuran subset yang lebih dari mencakup jumlah baris yang Anda butuhkan, lalu tambahkan a
Top()
. Idenya adalah Anda dapat membuat meja ginormous Anda tampak lebih kecil sebelum jenis mahal.Secara pribadi saya telah menggunakannya untuk membatasi ukuran meja saya. Jadi pada tabel sejuta baris yang melakukan
top(20)...TABLESAMPLE(20 PERCENT)
kueri turun menjadi 5.600 dibaca dalam 1600ms. Ada jugaREPEATABLE()
opsi di mana Anda dapat melewati "Benih" untuk pemilihan halaman. Ini akan menghasilkan pemilihan sampel yang stabil.Pokoknya, anggap saja ini harus ditambahkan ke diskusi. Semoga ini bisa membantu seseorang.
sumber
TABLESAMPLE()
berdasarkan berapa banyak data yang Anda miliki. Saya tidak berpikir ituTABLESAMPLE(x ROWS)
akan memastikan bahwa setidaknyax
baris dikembalikan karena dokumentasi mengatakan “Jumlah aktual baris yang dikembalikan dapat sangat bervariasi. Jika Anda menentukan jumlah kecil, seperti 5, Anda mungkin tidak menerima hasil dalam sampel. "- jadiROWS
sintaksnya masih benar-benar hanya bertopengPERCENT
di dalam?Banyak tabel memiliki kolom ID numerik terindeks yang relatif padat (beberapa nilai yang hilang).
Ini memungkinkan kami untuk menentukan rentang nilai yang ada, dan memilih baris menggunakan nilai ID yang dibuat secara acak dalam rentang itu. Ini bekerja paling baik ketika jumlah baris yang akan dikembalikan relatif kecil, dan kisaran nilai ID padat penduduk (sehingga kemungkinan menghasilkan nilai yang hilang cukup kecil).
Sebagai ilustrasi, kode berikut memilih 100 pengguna acak berbeda dari tabel Stack Overflow pengguna, yang memiliki 8.123.937 baris.
Langkah pertama adalah menentukan rentang nilai ID, operasi yang efisien karena indeks:
Rencana membaca satu baris dari setiap ujung indeks.
Sekarang kami menghasilkan 100 ID acak berbeda dalam rentang (dengan baris yang cocok di tabel pengguna) dan mengembalikan baris itu:
Rencana tersebut menunjukkan bahwa dalam hal ini 601 angka acak diperlukan untuk menemukan 100 baris yang cocok. Cukup cepat:
Cobalah di Explorer Data Stack Exchange.
sumber
Seperti yang saya jelaskan dalam artikel ini , untuk mengocok set hasil SQL, Anda perlu menggunakan panggilan fungsi database-spesifik.
Jadi, anggap kita memiliki tabel database berikut:
Dan baris-baris berikut dalam
song
tabel:Pada SQL Server, Anda perlu menggunakan
NEWID
fungsi, seperti yang diilustrasikan oleh contoh berikut:Saat menjalankan kueri SQL yang disebutkan di SQL Server, kita akan mendapatkan set hasil berikut:
sumber