i digunakan untuk selalu melakukan hal ini di php setelah hasil query dari sql ... ini mungkin jauh lebih cepat untuk pengolahan per solusi ini batas 1 embel
Lihat posting ini: SQL untuk Memilih baris acak dari tabel database . Itu melewati metode untuk melakukan ini di MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 dan Oracle (berikut ini disalin dari tautan itu):
Pilih baris acak dengan MySQL:
SELECTcolumnFROMtableORDERBY RAND()
LIMIT 1
Pilih baris acak dengan PostgreSQL:
SELECTcolumnFROMtableORDERBY RANDOM()
LIMIT 1
Pilih baris acak dengan Microsoft SQL Server:
SELECTTOP1columnFROMtableORDERBY NEWID()
Pilih baris acak dengan IBM DB2
SELECTcolumn, RAND()as IDX FROMtableORDERBY IDX FETCH FIRST 1ROWS ONLY
ORDER BY NEWID () tampaknya jauh lebih lambat di SQL Server. Kueri saya terlihat seperti: pilih 1.000 C.CustomerId, CL.LoginName dari Pelanggan C bergabung dengan LinkedAccount LA di C.CustomerId = LA.CustomerId bergabung dengan CustomerLogin CL di C.CustomerId = CL.CustomerId grup oleh C.CustomerId, CL. LoginName memiliki hitungan (*)> 1 pesanan oleh NEWID () Menghapus baris "pesanan oleh NEWID ()" mengembalikan hasil yang jauh lebih cepat.
Ben Power
3
Untuk SQLite gunakan fungsi RANDOM ().
Slam
10
Solusi ini tidak berskala. Mereka O(n)dengan nmenjadi jumlah catatan dalam tabel. Bayangkan Anda memiliki 1 juta catatan, apakah Anda benar-benar ingin menghasilkan 1 juta angka acak atau id unik? Saya lebih suka menggunakan COUNT()dan melibatkan itu dalam LIMITekspresi baru dengan nomor acak tunggal.
Christian Hujer
174
Solusi seperti Jeremies:
SELECT*FROMtableORDERBY RAND() LIMIT 1
bekerja, tetapi mereka membutuhkan pemindaian berurutan dari semua tabel (karena nilai acak yang terkait dengan setiap baris perlu dihitung - sehingga yang terkecil dapat ditentukan), yang bisa sangat lambat bahkan untuk tabel berukuran sedang. Rekomendasi saya adalah menggunakan semacam kolom angka yang diindeks (banyak tabel memiliki ini sebagai kunci utama mereka), dan kemudian menulis sesuatu seperti:
SELECT*FROMtableWHERE num_value >= RAND()*(SELECT MAX (num_value )FROMtable)ORDERBY num_value LIMIT 1
Ini berfungsi dalam waktu logaritmik, terlepas dari ukuran tabel, jika num_valuediindeks. Satu peringatan: ini mengasumsikan bahwa num_valueterdistribusi secara merata dalam kisaran 0..MAX(num_value). Jika dataset Anda sangat menyimpang dari asumsi ini, Anda akan mendapatkan hasil yang miring (beberapa baris akan muncul lebih sering daripada yang lain).
Saran kedua tidak acak. Anda tidak dapat memprediksi baris yang akan dipilih, tetapi jika Anda harus bertaruh, Anda akan bertaruh pada baris kedua. Dan Anda tidak akan pernah bertaruh pada baris terakhir, semakin kecil kemungkinannya untuk memilih apa pun distribusi num_value Anda dan seberapa besar tabel Anda.
Etienne Racine
1
Saya tahu bahwa biasanya fungsi RAND () tidak berkualitas sangat tinggi, tetapi selain itu dapatkah Anda menjelaskan mengapa pemilihannya tidak acak?
Grey Panther
13
Yang pertama adalah SALAH dalam SQL Server. Fungsi RAND () dipanggil hanya sekali per kueri, tidak satu kali per baris. Jadi selalu memilih baris pertama (coba).
Jeff Walker Code Ranger
3
Yang kedua juga mengasumsikan bahwa semua baris diperhitungkan: mungkin saja akan memilih baris yang telah dihapus.
Sam Rueby
3
@ Sam.Rueby Sebenarnya, num_value> = RAND () ... limit 1 memastikan bahwa baris kosong akan dilewati sampai menemukan baris yang ada.
ghord
62
Saya tidak tahu seberapa efisien ini, tetapi saya telah menggunakannya sebelumnya:
SELECTTOP1*FROM MyTable ORDERBY newid()
Karena GUID cukup acak, pemesanan berarti Anda mendapatkan baris acak.
Saya menggunakan server MS SQL, SELECT TOP 1 * FROM some_table_name ORDER BY NEWID () bekerja sangat baik untuk saya, terima kasih atas sarannya guys!
Itu persis sama denganORDER BY RAND() LIMIT 1
Ken Bloom
6
Ini juga sangat spesifik karena menggunakan TOP 1dan newid().
Gray
12
Ini ide yang buruk. Metode ini tidak akan menggunakan indeks kecuali setiap kolom diindeks secara individual. Tabel dengan 100 juta catatan bisa memakan waktu yang sangat lama untuk mendapatkan satu catatan.
Beralih
1
@Switch dan solusi apa yang akan Anda usulkan?
Akmal Salikhov
31
ORDERBY NEWID()
mengambil 7.4 milliseconds
WHERE num_value >= RAND()*(SELECT MAX(num_value)FROMtable)
Opsi kedua tidak akan memilih baris terakhir. Saya tidak tahu mengapa - hanya menunjukkannya.
Voldemort
7
@Oldemort: rand()mengembalikan angka titik-mengambang di nmana 0 < n < 1. Dengan asumsi num_valuebilangan bulat, nilai pengembalian dari rand() * max(num_value)juga akan dipaksa ke bilangan bulat, sehingga memotong apa pun setelah titik desimal. Karenanya, rand() * max(num_value)akan selalu kurang dari max(num_value), itulah sebabnya baris terakhir tidak akan pernah dipilih.
Ian Kemp
Saya tidak akan efisien jika data saya sering dihapus - jika saya menemukan celah, saya harus menjalankan kembali seluruh permintaan.
Loic Coenen
1
@IanKemp pertanyaan bodoh, lalu mengapa tidak menggunakan SELECT MAX (num_value) +1? Karena rand (atau ACAK dalam kebanyakan kasus) mengembalikan [0,1), Anda akan mendapatkan rentang nilai penuh. Juga, ya, Anda benar, harus memperbaiki kueri.
tekHedd
13
Anda tidak mengatakan server mana yang Anda gunakan. Di versi SQL Server yang lebih lama, Anda dapat menggunakan ini:
selecttop1*from mytable orderby newid()
Di SQL Server 2005 dan lebih tinggi, Anda bisa menggunakan TABLESAMPLEuntuk mendapatkan sampel acak yang dapat diulang:
SELECT FirstName, LastName
FROM Contact
TABLESAMPLE (1ROWS);
newid () / order by akan berfungsi, tetapi akan sangat mahal untuk set hasil yang besar karena harus menghasilkan id untuk setiap baris, dan kemudian mengurutkannya.
TABLESAMPLE () bagus dari sudut pandang kinerja, tetapi Anda akan mendapatkan hasil yang berkelompok (semua baris pada halaman akan dikembalikan).
Untuk sampel acak benar yang berkinerja lebih baik, cara terbaik adalah menyaring baris secara acak. Saya menemukan contoh kode berikut dalam artikel SQL Server Books Online yang Membatasi Set Hasil dengan Menggunakan TABLESAMPLE :
Jika Anda benar-benar ingin sampel acak baris individual, ubah kueri Anda untuk memfilter baris secara acak, alih-alih menggunakan TABLESAMPLE. Misalnya, kueri berikut menggunakan fungsi NEWID untuk mengembalikan sekitar satu persen dari baris tabel Sales.SalesOrderDetail:
Kolom SalesOrderID termasuk dalam ekspresi CHECKSUM sehingga NEWID () mengevaluasi satu kali per baris untuk mencapai pengambilan sampel pada basis per baris. Ekspresi CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) mengevaluasi nilai float acak antara 0 dan 1.
Saat dijalankan melawan tabel dengan 1.000.000 baris, berikut ini adalah hasil saya:
SETSTATISTICS TIME ONSETSTATISTICS IO ON/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/SELECTTOP1PERCENT Number
FROM Numbers
ORDERBY newid()/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/SELECT Number
FROM Numbers
TABLESAMPLE (1PERCENT)/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/SELECT Number
FROM Numbers
WHERE0.01>= CAST(CHECKSUM(NEWID(), Number)&0x7fffffffAS float)/ CAST (0x7fffffffAS int)SETSTATISTICS IO OFFSETSTATISTICS TIME OFF
Jika Anda bisa menggunakan TABLESAMPLE, itu akan memberi Anda kinerja terbaik. Kalau tidak, gunakan metode newid () / filter. newid () / order oleh harus menjadi pilihan terakhir jika Anda memiliki set hasil yang besar.
Jika memungkinkan, gunakan pernyataan tersimpan untuk menghindari inefisiensi kedua indeks pada RND () dan membuat bidang nomor catatan.
SIAPKAN RandomRecord DARI "SELECT * FROM table LIMIT?, 1";
SET @ n = LANTAI (RAND () * (SELECT COUNT (*) FROM table));
EXECUTE RandomRecord MENGGUNAKAN @n;
Solusi ini juga menangani pengembalian baris acak ketika nilai numerik yang diindeks digunakan di mana klausa di atas tidak terdistribusi secara merata; jadi walaupun itu membutuhkan waktu yang hampir sama (konstan) dengan menggunakan mana id_value> = RAND () * MAX (id_value), itu lebih baik.
guido
Sejauh yang saya tahu ini tidak berjalan dalam waktu yang konstan, itu berjalan dalam waktu linier. Dalam kasus terburuk, @n sama dengan jumlah baris dalam tabel, dan "SELECT * FROM table LIMIT?, 1" mengevaluasi @n - 1 baris hingga mencapai yang terakhir.
Andres Riofrio
3
Cara terbaik adalah meletakkan nilai acak di kolom baru hanya untuk tujuan itu, dan menggunakan sesuatu seperti ini (pseude code + SQL):
randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")
Ini adalah solusi yang digunakan oleh kode MediaWiki. Tentu saja, ada beberapa bias terhadap nilai yang lebih kecil, tetapi mereka menemukan bahwa itu cukup untuk membungkus nilai acak menjadi nol ketika tidak ada baris yang diambil.
solusi newid () mungkin memerlukan pemindaian tabel penuh sehingga setiap baris dapat diberi panduan baru, yang akan jauh lebih sedikit performanya.
rand () solusi mungkin tidak berfungsi sama sekali (yaitu dengan MSSQL) karena fungsi akan dievaluasi sekali saja, dan setiap baris akan diberi nomor "acak" yang sama.
Melilit saat Anda mendapatkan 0 hasil memberikan sampel acak yang terbukti (bukan hanya "cukup baik"). Solusi ini hampir mencapai permintaan multi-baris (pikirkan "pengocokan partai"). Masalahnya adalah bahwa hasilnya cenderung dipilih dalam kelompok yang sama berulang kali. Untuk menyiasatinya, Anda perlu mendistribusikan kembali nomor acak yang baru saja Anda gunakan. Anda dapat menipu dengan melacak randomNo dan mengaturnya ke maks (keacakan) dari hasil, tetapi kemudian p (baris i pada kueri 1 DAN baris i pada kueri 2) == 0, yang tidak adil. Biarkan saya melakukan beberapa matematika, dan saya akan kembali kepada Anda dengan skema yang benar-benar adil.
alsuren
3
Untuk SQL Server 2005 dan 2008, jika kita ingin sampel acak dari masing-masing baris (dari Books Online ):
SELECT ID FROMTABLEWHERE ID >= My_Generated_Random ORDERBY ID LIMIT 1
Perhatikan bahwa ia akan memeriksa setiap baris yang Idnya EQUAL atau LEBIH TINGGI dari nilai yang dipilih. Dimungkinkan juga untuk mencari baris di tabel, dan mendapatkan ID yang sama atau lebih rendah daripada My_Generated_Random, lalu ubah kueri seperti ini:
SELECT ID FROMTABLEWHERE ID <= My_Generated_Random ORDERBY ID DESC LIMIT 1
Apa yang akan terjadi jika ID acak yang dihasilkan tidak ada dalam tabel lagi? Baris yang dihapus atau pasif yang tidak ingin Anda tampilkan kepada pengguna akan menyebabkan masalah.
Ebleme
Tidak ada. Anda mendapatkan nomor id TERTUTUP, tidak tepat,. Jika Anda menganggap id = 1 akan dihapus, tukar 1 dengan minimum.
forsberg
2
Seperti yang ditunjukkan dalam komentar @ BillKarwin pada jawaban @ cnu ...
Ketika menggabungkan dengan LIMIT, saya telah menemukan bahwa kinerjanya jauh lebih baik (setidaknya dengan PostgreSQL 9.1) untuk BERGABUNG dengan pemesanan acak daripada langsung memesan baris aktual: misalnya
SELECT*FROM tbl_post AS t
JOIN...JOIN(SELECT id, CAST(-2147483648* RANDOM()AS integer)AS rand
FROM tbl_post
WHERE create_time >=1349928000) r ON r.id = t.id
WHERE create_time >=1349928000AND...ORDERBY r.rand
LIMIT 100
Pastikan saja bahwa 'r' menghasilkan nilai 'rand' untuk setiap nilai kunci yang mungkin dalam kueri kompleks yang digabungkan dengannya tetapi masih membatasi jumlah baris 'r' jika memungkinkan.
CAST sebagai Integer sangat membantu untuk PostgreSQL 9.2 yang memiliki optimasi pengurutan khusus untuk tipe floating dan integer presisi tunggal.
Sebagian besar solusi di sini bertujuan untuk menghindari penyortiran, tetapi mereka masih perlu melakukan pemindaian berurutan di atas tabel.
Ada juga cara untuk menghindari pemindaian berurutan dengan beralih ke pemindaian indeks. Jika Anda tahu nilai indeks dari baris acak Anda, Anda bisa mendapatkan hasilnya hampir secara instan. Masalahnya adalah - bagaimana menebak nilai indeks.
Solusi berikut ini berfungsi pada PostgreSQL 8.4:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))
limit 1;
Saya solusi di atas Anda menebak 10 berbagai nilai indeks acak dari kisaran 0 .. [nilai terakhir id].
Angka 10 arbitrer - Anda dapat menggunakan 100 atau 1000 karena (luar biasa) tidak memiliki dampak besar pada waktu respons.
Ada juga satu masalah - jika Anda memiliki id jarang Anda mungkin kehilangan . Solusinya adalah memiliki rencana cadangan :) Dalam hal ini pesanan lama murni dengan permintaan acak (). Ketika gabungan id terlihat seperti ini:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))unionall(select*from cms_refs orderby random() limit 1)
limit 1;
Bukan klausa SEMUA serikat buruh . Dalam hal ini jika bagian pertama mengembalikan data apa pun, bagian kedua TIDAK PERNAH dieksekusi!
Pada akhirnya, tetapi tiba di sini melalui Google, jadi demi keturunan, saya akan menambahkan solusi alternatif.
Pendekatan lain adalah menggunakan TOP dua kali, dengan pesanan bergantian. Saya tidak tahu apakah itu "SQL murni", karena menggunakan variabel di TOP, tetapi berfungsi di SQL Server 2008. Berikut adalah contoh yang saya gunakan terhadap tabel kata kamus, jika saya ingin kata acak.
SELECTTOP1
word
FROM(SELECTTOP(@idx)
word
FROM
dbo.DictionaryAbridged WITH(NOLOCK)ORDERBY
word DESC)AS D
ORDERBY
word ASC
Tentu saja, @idx adalah beberapa bilangan bulat yang dihasilkan secara acak yang berkisar dari 1 hingga COUNT (*) pada tabel target, secara inklusif. Jika kolom Anda diindeks, Anda juga akan mendapat manfaatnya. Keuntungan lain adalah Anda dapat menggunakannya dalam suatu fungsi, karena NEWID () tidak diizinkan.
Terakhir, kueri di atas berjalan sekitar 1/10 dari waktu exec dari NEWID () - jenis kueri pada tabel yang sama. YYMV.
Setelah menguji banyak jawaban saya percaya bahwa ini adalah yang terbaik. Tampaknya cepat dan mengambil nomor acak yang bagus setiap kali. Tampaknya mirip dengan saran kedua @GreyPanther di atas, tetapi jawaban ini memilih lebih banyak angka acak.
Jeff Baker
1
Belum melihat variasi ini dalam jawaban. Saya memiliki kendala tambahan di mana saya perlu, diberikan benih awal, untuk memilih set baris yang sama setiap kali.
NewId()lebih lambat dari biasanya rand(checksum(*)), jadi Anda mungkin tidak ingin menggunakannya melawan set rekaman besar.
Pilihan dengan Benih Awal:
declare@seed int
set@seed = Year(getdate())* month(getdate())/* any other initial seed here */selecttop10percent*from table_name
orderby rand(checksum(*)% seed)/* any other math function here */
Jika Anda perlu memilih set yang sama dengan seed, ini sepertinya berhasil.
Dalam SQL Server Anda dapat menggabungkan TABLESAMPLE dengan NEWID () untuk mendapatkan keacakan yang cukup bagus dan masih memiliki kecepatan. Ini sangat berguna jika Anda benar-benar hanya menginginkan 1, atau sejumlah kecil, baris.
Dengan SQL Server 2012+ Anda dapat menggunakan kueri FETCH OFFSET untuk melakukan ini untuk satu baris acak
select*from MyTable ORDERBY id OFFSET n ROWFETCH NEXT 1ROWS ONLY
di mana id adalah kolom identitas, dan n adalah baris yang Anda inginkan - dihitung sebagai angka acak antara 0 dan hitung () - 1 dari tabel (offset 0 adalah baris pertama setelah semua)
Ini berfungsi dengan lubang di data tabel, selama Anda memiliki indeks untuk bekerja dengan untuk klausa ORDER BY. Ini juga sangat baik untuk keacakan - saat Anda berusaha untuk lulus tetapi orang-orang cekatan dalam metode lain tidak ada. Selain itu kinerjanya cukup baik, pada dataset yang lebih kecil, ia bertahan dengan baik, meskipun saya belum mencoba tes kinerja serius terhadap beberapa juta baris.
Sepuluh tahun yang lalu (2005) beberapa pria mengatakan bahwa menggunakan ORDER BY RAND()itu salah ...
trejder
0
Saya harus setuju dengan CD-MAN: Menggunakan "ORDER BY RAND ()" akan bekerja dengan baik untuk tabel kecil atau ketika Anda melakukan SELECT Anda hanya beberapa kali.
Saya juga menggunakan teknik "num_value> = RAND () * ...", dan jika saya benar-benar ingin mendapatkan hasil acak, saya memiliki kolom "acak" khusus dalam tabel yang saya perbarui sekali sehari atau lebih. Proses UPDATE tunggal itu akan memakan waktu (terutama karena Anda harus memiliki indeks pada kolom itu), tetapi itu jauh lebih cepat daripada membuat angka acak untuk setiap baris setiap kali pilih dijalankan.
Hati-hati karena TableSample tidak benar-benar mengembalikan sampel acak baris. Ini mengarahkan kueri Anda untuk melihat sampel acak dari halaman 8KB yang membentuk baris Anda. Kemudian, kueri Anda dieksekusi terhadap data yang terkandung di halaman ini. Karena cara data dapat dikelompokkan pada halaman ini (urutan penyisipan, dll), ini dapat menyebabkan data yang sebenarnya bukan sampel acak.
Tampaknya banyak ide yang terdaftar masih menggunakan pemesanan
Namun, jika Anda menggunakan tabel sementara, Anda dapat menetapkan indeks acak (seperti banyak solusi yang disarankan), dan kemudian ambil yang pertama yang lebih besar dari angka arbitrer antara 0 dan 1.
Misalnya (untuk DB2):
WITH TEMP AS(SELECT COMLUMN, RAND()AS IDX FROMTABLE)SELECTCOLUMNFROMTABLEWHERE IDX >.5FETCH FIRST 1ROW ONLY
Setelah mempertimbangkan solusi ini, saya telah menemukan cacat mendasar dalam logika saya. Ini akan secara konsisten mengembalikan nilai pengaturan kecil yang sama, di dekat bagian awal tabel, karena saya berasumsi bahwa jika ada distribusi rata antara 0 dan 1, ada kemungkinan 50% bahwa baris pertama akan memenuhi kriteria itu.
Ada solusi yang lebih baik untuk Oracle daripada menggunakan dbms_random.value, sementara itu membutuhkan pemindaian penuh untuk memesan baris oleh dbms_random.value dan itu cukup lambat untuk tabel besar.
Untuk SQL Server 2005 dan di atas, memperluas jawaban @ GreyPanther untuk kasus-kasus ketika num_valuetidak memiliki nilai kontinu. Ini juga berfungsi untuk kasus-kasus ketika kita belum mendistribusikan dataset secara merata dan kapan num_valuebukan angka melainkan pengenal unik.
WITH CTE_Table (SelRow, num_value)AS(SELECT ROW_NUMBER()OVER(ORDERBY ID)AS SelRow, num_value FROMtable)SELECT*FROMtableWhere num_value =(SELECTTOP1 num_value FROM CTE_Table WHERE SelRow >= RAND()*(SELECT MAX(SelRow)FROM CTE_Table))
Jawaban:
Lihat posting ini: SQL untuk Memilih baris acak dari tabel database . Itu melewati metode untuk melakukan ini di MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 dan Oracle (berikut ini disalin dari tautan itu):
Pilih baris acak dengan MySQL:
Pilih baris acak dengan PostgreSQL:
Pilih baris acak dengan Microsoft SQL Server:
Pilih baris acak dengan IBM DB2
Pilih catatan acak dengan Oracle:
sumber
order by rand()
atau setara dalam semua dbs: |. juga disebutkan di sini .ORDER BY RAND()
itu salah ...O(n)
dengann
menjadi jumlah catatan dalam tabel. Bayangkan Anda memiliki 1 juta catatan, apakah Anda benar-benar ingin menghasilkan 1 juta angka acak atau id unik? Saya lebih suka menggunakanCOUNT()
dan melibatkan itu dalamLIMIT
ekspresi baru dengan nomor acak tunggal.Solusi seperti Jeremies:
bekerja, tetapi mereka membutuhkan pemindaian berurutan dari semua tabel (karena nilai acak yang terkait dengan setiap baris perlu dihitung - sehingga yang terkecil dapat ditentukan), yang bisa sangat lambat bahkan untuk tabel berukuran sedang. Rekomendasi saya adalah menggunakan semacam kolom angka yang diindeks (banyak tabel memiliki ini sebagai kunci utama mereka), dan kemudian menulis sesuatu seperti:
Ini berfungsi dalam waktu logaritmik, terlepas dari ukuran tabel, jika
num_value
diindeks. Satu peringatan: ini mengasumsikan bahwanum_value
terdistribusi secara merata dalam kisaran0..MAX(num_value)
. Jika dataset Anda sangat menyimpang dari asumsi ini, Anda akan mendapatkan hasil yang miring (beberapa baris akan muncul lebih sering daripada yang lain).sumber
Saya tidak tahu seberapa efisien ini, tetapi saya telah menggunakannya sebelumnya:
Karena GUID cukup acak, pemesanan berarti Anda mendapatkan baris acak.
sumber
ORDER BY RAND() LIMIT 1
TOP 1
dannewid()
.mengambil
7.4 milliseconds
mengambil
0.0065 milliseconds
!Saya pasti akan menggunakan metode yang terakhir.
sumber
rand()
mengembalikan angka titik-mengambang din
mana0 < n < 1
. Dengan asumsinum_value
bilangan bulat, nilai pengembalian darirand() * max(num_value)
juga akan dipaksa ke bilangan bulat, sehingga memotong apa pun setelah titik desimal. Karenanya,rand() * max(num_value)
akan selalu kurang darimax(num_value)
, itulah sebabnya baris terakhir tidak akan pernah dipilih.Anda tidak mengatakan server mana yang Anda gunakan. Di versi SQL Server yang lebih lama, Anda dapat menggunakan ini:
Di SQL Server 2005 dan lebih tinggi, Anda bisa menggunakan
TABLESAMPLE
untuk mendapatkan sampel acak yang dapat diulang:sumber
Untuk SQL Server
newid () / order by akan berfungsi, tetapi akan sangat mahal untuk set hasil yang besar karena harus menghasilkan id untuk setiap baris, dan kemudian mengurutkannya.
TABLESAMPLE () bagus dari sudut pandang kinerja, tetapi Anda akan mendapatkan hasil yang berkelompok (semua baris pada halaman akan dikembalikan).
Untuk sampel acak benar yang berkinerja lebih baik, cara terbaik adalah menyaring baris secara acak. Saya menemukan contoh kode berikut dalam artikel SQL Server Books Online yang Membatasi Set Hasil dengan Menggunakan TABLESAMPLE :
Saat dijalankan melawan tabel dengan 1.000.000 baris, berikut ini adalah hasil saya:
Jika Anda bisa menggunakan TABLESAMPLE, itu akan memberi Anda kinerja terbaik. Kalau tidak, gunakan metode newid () / filter. newid () / order oleh harus menjadi pilihan terakhir jika Anda memiliki set hasil yang besar.
sumber
Jika memungkinkan, gunakan pernyataan tersimpan untuk menghindari inefisiensi kedua indeks pada RND () dan membuat bidang nomor catatan.
sumber
Cara terbaik adalah meletakkan nilai acak di kolom baru hanya untuk tujuan itu, dan menggunakan sesuatu seperti ini (pseude code + SQL):
Ini adalah solusi yang digunakan oleh kode MediaWiki. Tentu saja, ada beberapa bias terhadap nilai yang lebih kecil, tetapi mereka menemukan bahwa itu cukup untuk membungkus nilai acak menjadi nol ketika tidak ada baris yang diambil.
solusi newid () mungkin memerlukan pemindaian tabel penuh sehingga setiap baris dapat diberi panduan baru, yang akan jauh lebih sedikit performanya.
rand () solusi mungkin tidak berfungsi sama sekali (yaitu dengan MSSQL) karena fungsi akan dievaluasi sekali saja, dan setiap baris akan diberi nomor "acak" yang sama.
sumber
Untuk SQL Server 2005 dan 2008, jika kita ingin sampel acak dari masing-masing baris (dari Books Online ):
sumber
Insted menggunakan RAND (), karena tidak dianjurkan , Anda cukup mendapatkan ID maks (= Max):
dapatkan secara acak antara 1..Max (= My_Generated_Random)
dan kemudian jalankan SQL ini:
Perhatikan bahwa ia akan memeriksa setiap baris yang Idnya EQUAL atau LEBIH TINGGI dari nilai yang dipilih. Dimungkinkan juga untuk mencari baris di tabel, dan mendapatkan ID yang sama atau lebih rendah daripada My_Generated_Random, lalu ubah kueri seperti ini:
sumber
Seperti yang ditunjukkan dalam komentar @ BillKarwin pada jawaban @ cnu ...
Ketika menggabungkan dengan LIMIT, saya telah menemukan bahwa kinerjanya jauh lebih baik (setidaknya dengan PostgreSQL 9.1) untuk BERGABUNG dengan pemesanan acak daripada langsung memesan baris aktual: misalnya
Pastikan saja bahwa 'r' menghasilkan nilai 'rand' untuk setiap nilai kunci yang mungkin dalam kueri kompleks yang digabungkan dengannya tetapi masih membatasi jumlah baris 'r' jika memungkinkan.
CAST sebagai Integer sangat membantu untuk PostgreSQL 9.2 yang memiliki optimasi pengurutan khusus untuk tipe floating dan integer presisi tunggal.
sumber
Sebagian besar solusi di sini bertujuan untuk menghindari penyortiran, tetapi mereka masih perlu melakukan pemindaian berurutan di atas tabel.
Ada juga cara untuk menghindari pemindaian berurutan dengan beralih ke pemindaian indeks. Jika Anda tahu nilai indeks dari baris acak Anda, Anda bisa mendapatkan hasilnya hampir secara instan. Masalahnya adalah - bagaimana menebak nilai indeks.
Solusi berikut ini berfungsi pada PostgreSQL 8.4:
Saya solusi di atas Anda menebak 10 berbagai nilai indeks acak dari kisaran 0 .. [nilai terakhir id].
Angka 10 arbitrer - Anda dapat menggunakan 100 atau 1000 karena (luar biasa) tidak memiliki dampak besar pada waktu respons.
Ada juga satu masalah - jika Anda memiliki id jarang Anda mungkin kehilangan . Solusinya adalah memiliki rencana cadangan :) Dalam hal ini pesanan lama murni dengan permintaan acak (). Ketika gabungan id terlihat seperti ini:
Bukan klausa SEMUA serikat buruh . Dalam hal ini jika bagian pertama mengembalikan data apa pun, bagian kedua TIDAK PERNAH dieksekusi!
sumber
Pada akhirnya, tetapi tiba di sini melalui Google, jadi demi keturunan, saya akan menambahkan solusi alternatif.
Pendekatan lain adalah menggunakan TOP dua kali, dengan pesanan bergantian. Saya tidak tahu apakah itu "SQL murni", karena menggunakan variabel di TOP, tetapi berfungsi di SQL Server 2008. Berikut adalah contoh yang saya gunakan terhadap tabel kata kamus, jika saya ingin kata acak.
Tentu saja, @idx adalah beberapa bilangan bulat yang dihasilkan secara acak yang berkisar dari 1 hingga COUNT (*) pada tabel target, secara inklusif. Jika kolom Anda diindeks, Anda juga akan mendapat manfaatnya. Keuntungan lain adalah Anda dapat menggunakannya dalam suatu fungsi, karena NEWID () tidak diizinkan.
Terakhir, kueri di atas berjalan sekitar 1/10 dari waktu exec dari NEWID () - jenis kueri pada tabel yang sama. YYMV.
sumber
Anda juga dapat mencoba menggunakan
new id()
fungsi.Tulis saja kueri Anda dan gunakan urutan berdasarkan
new id()
fungsi. Ini cukup acak.sumber
Untuk MySQL untuk mendapatkan catatan acak
Lebih detail http://jan.kneschke.de/projects/mysql/order-by-rand/
sumber
Belum melihat variasi ini dalam jawaban. Saya memiliki kendala tambahan di mana saya perlu, diberikan benih awal, untuk memilih set baris yang sama setiap kali.
Untuk MS SQL:
Contoh minimum:
Waktu pelaksanaan normal: 1,00
Contoh NewId ():
Waktu pelaksanaan normal: 1.02
NewId()
lebih lambat dari biasanyarand(checksum(*))
, jadi Anda mungkin tidak ingin menggunakannya melawan set rekaman besar.Pilihan dengan Benih Awal:
Jika Anda perlu memilih set yang sama dengan seed, ini sepertinya berhasil.
sumber
Dalam MSSQL (diuji pada 11.0.5569) menggunakan
secara signifikan lebih cepat daripada
sumber
Dalam SQL Server Anda dapat menggabungkan TABLESAMPLE dengan NEWID () untuk mendapatkan keacakan yang cukup bagus dan masih memiliki kecepatan. Ini sangat berguna jika Anda benar-benar hanya menginginkan 1, atau sejumlah kecil, baris.
sumber
Dengan SQL Server 2012+ Anda dapat menggunakan kueri FETCH OFFSET untuk melakukan ini untuk satu baris acak
di mana id adalah kolom identitas, dan n adalah baris yang Anda inginkan - dihitung sebagai angka acak antara 0 dan hitung () - 1 dari tabel (offset 0 adalah baris pertama setelah semua)
Ini berfungsi dengan lubang di data tabel, selama Anda memiliki indeks untuk bekerja dengan untuk klausa ORDER BY. Ini juga sangat baik untuk keacakan - saat Anda berusaha untuk lulus tetapi orang-orang cekatan dalam metode lain tidak ada. Selain itu kinerjanya cukup baik, pada dataset yang lebih kecil, ia bertahan dengan baik, meskipun saya belum mencoba tes kinerja serius terhadap beberapa juta baris.
sumber
sumber
ORDER BY RAND()
itu salah ...Saya harus setuju dengan CD-MAN: Menggunakan "ORDER BY RAND ()" akan bekerja dengan baik untuk tabel kecil atau ketika Anda melakukan SELECT Anda hanya beberapa kali.
Saya juga menggunakan teknik "num_value> = RAND () * ...", dan jika saya benar-benar ingin mendapatkan hasil acak, saya memiliki kolom "acak" khusus dalam tabel yang saya perbarui sekali sehari atau lebih. Proses UPDATE tunggal itu akan memakan waktu (terutama karena Anda harus memiliki indeks pada kolom itu), tetapi itu jauh lebih cepat daripada membuat angka acak untuk setiap baris setiap kali pilih dijalankan.
sumber
Hati-hati karena TableSample tidak benar-benar mengembalikan sampel acak baris. Ini mengarahkan kueri Anda untuk melihat sampel acak dari halaman 8KB yang membentuk baris Anda. Kemudian, kueri Anda dieksekusi terhadap data yang terkandung di halaman ini. Karena cara data dapat dikelompokkan pada halaman ini (urutan penyisipan, dll), ini dapat menyebabkan data yang sebenarnya bukan sampel acak.
Lihat: http://www.mssqltips.com/tip.asp?tip=1308
Halaman MSDN ini untuk TableSample termasuk contoh cara menghasilkan sampel data acak yang sebenarnya.
http://msdn.microsoft.com/en-us/library/ms189108.aspx
sumber
Tampaknya banyak ide yang terdaftar masih menggunakan pemesanan
Namun, jika Anda menggunakan tabel sementara, Anda dapat menetapkan indeks acak (seperti banyak solusi yang disarankan), dan kemudian ambil yang pertama yang lebih besar dari angka arbitrer antara 0 dan 1.
Misalnya (untuk DB2):
sumber
Cara sederhana dan efisien dari http://akinas.com/pages/en/blog/mysql_random_row/
sumber
Ada solusi yang lebih baik untuk Oracle daripada menggunakan dbms_random.value, sementara itu membutuhkan pemindaian penuh untuk memesan baris oleh dbms_random.value dan itu cukup lambat untuk tabel besar.
Gunakan ini sebagai gantinya:
sumber
Untuk Firebird:
sumber
Untuk SQL Server 2005 dan di atas, memperluas jawaban @ GreyPanther untuk kasus-kasus ketika
num_value
tidak memiliki nilai kontinu. Ini juga berfungsi untuk kasus-kasus ketika kita belum mendistribusikan dataset secara merata dan kapannum_value
bukan angka melainkan pengenal unik.sumber
Fungsi acak dari sql dapat membantu. Juga jika Anda ingin membatasi hanya satu baris, tambahkan saja pada akhirnya.
sumber