Menurut MSDN , Median tidak tersedia sebagai fungsi agregat di Transact-SQL. Namun, saya ingin mengetahui apakah mungkin untuk membuat fungsionalitas ini (menggunakan fungsi Buat Agregat , fungsi yang ditentukan pengguna, atau beberapa metode lain).
Apa yang akan menjadi cara terbaik (jika mungkin) untuk melakukan ini - memungkinkan untuk perhitungan nilai median (dengan asumsi tipe data numerik) dalam permintaan agregat?
sql
sql-server
aggregate-functions
median
Yaakov Ellis
sumber
sumber
Jawaban:
PEMBARUAN 2019: Dalam 10 tahun sejak saya menulis jawaban ini, lebih banyak solusi telah ditemukan yang dapat memberikan hasil yang lebih baik. Juga, rilis SQL Server sejak saat itu (terutama SQL 2012) telah memperkenalkan fitur T-SQL baru yang dapat digunakan untuk menghitung median. Rilis SQL Server juga telah meningkatkan optimizer kueri yang dapat memengaruhi berbagai solusi median. Net-net, posting asli 2009 saya masih OK tapi mungkin ada solusi yang lebih baik untuk aplikasi SQL Server modern. Lihatlah artikel ini dari 2012 yang merupakan sumber yang bagus: https://sqlperformance.com/2012/08/t-sql-queries/median
Artikel ini menemukan pola berikut ini jauh lebih cepat daripada semua alternatif lain, setidaknya pada skema sederhana yang mereka uji. Solusi ini 373x lebih cepat (!!!) daripada solusi paling lambat (
PERCENTILE_CONT
) yang diuji. Perhatikan bahwa trik ini memerlukan dua kueri terpisah yang mungkin tidak praktis dalam semua kasus. Ini juga membutuhkan SQL 2012 atau yang lebih baru.Tentu saja, hanya karena satu tes pada satu skema di 2012 menghasilkan hasil yang bagus, jarak tempuh Anda dapat bervariasi, terutama jika Anda menggunakan SQL Server 2014 atau lebih baru. Jika perf penting untuk perhitungan median Anda, saya sangat menyarankan mencoba dan menguji beberapa opsi yang direkomendasikan dalam artikel itu untuk memastikan bahwa Anda telah menemukan yang terbaik untuk skema Anda.
Saya juga sangat berhati-hati dalam menggunakan fungsi (baru dalam SQL Server 2012)
PERCENTILE_CONT
yang direkomendasikan dalam salah satu jawaban lain untuk pertanyaan ini, karena artikel yang ditautkan di atas menemukan bahwa fungsi bawaan ini 373x lebih lambat daripada solusi tercepat. Mungkin perbedaan ini telah diperbaiki dalam 7 tahun sejak itu, tetapi secara pribadi saya tidak akan menggunakan fungsi ini di atas meja besar sampai saya memverifikasi kinerjanya vs solusi lain.ASLI 2009 POST DI BAWAH INI:
Ada banyak cara untuk melakukan ini, dengan kinerja yang sangat beragam. Inilah salah satu solusi yang dioptimalkan dengan sangat baik, dari Medians, ROW_NUMBERs, dan kinerja . Ini adalah solusi yang sangat optimal ketika datang ke I / O aktual yang dihasilkan selama eksekusi - ini terlihat lebih mahal daripada solusi lain, tetapi sebenarnya jauh lebih cepat.
Halaman itu juga berisi diskusi tentang solusi lain dan detail pengujian kinerja. Perhatikan penggunaan kolom unik sebagai disambiguator jika ada beberapa baris dengan nilai yang sama dari kolom median.
Seperti halnya semua skenario kinerja database, selalu mencoba menguji solusi dengan data nyata pada perangkat keras nyata - Anda tidak pernah tahu kapan perubahan ke pengoptimal SQL Server atau kekhasan di lingkungan Anda akan membuat solusi yang biasanya cepat lebih lambat.
sumber
Jika Anda menggunakan SQL 2005 atau lebih baik ini adalah perhitungan median bagus, sederhana untuk satu kolom dalam tabel:
sumber
select gid, median(score) from T group by gid
. Apakah Anda memerlukan subquery berkorelasi untuk itu?Di SQL Server 2012 Anda harus menggunakan PERCENTILE_CONT :
Lihat juga: http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-introduced-in-sql-server-2012/
sumber
DISTINCT
atauGROUPY BY SalesOrderID
? Kalau tidak, Anda akan memiliki banyak baris duplikat.PERCENTILE_DISC
Jawaban cepat asli saya adalah:
Ini akan memberi Anda kisaran median dan interkuartil dalam satu gerakan. Jika Anda benar-benar hanya ingin satu baris yang merupakan median maka batalkan komentar di mana klausa.
Ketika Anda memasukkannya ke dalam rencana penjelasan, 60% pekerjaannya menyortir data yang tidak dapat dihindari saat menghitung statistik dependen posisi seperti ini.
Saya telah mengubah jawaban untuk mengikuti saran luar biasa dari Robert Ševčík-Robajz dalam komentar di bawah:
Ini harus menghitung nilai median dan persentil yang benar ketika Anda memiliki jumlah item data yang genap. Sekali lagi, batalkan komentar di mana klausa akhir jika Anda hanya ingin median dan bukan seluruh distribusi persentil.
sumber
Bahkan lebih baik:
Dari sang master sendiri, Itzik Ben-Gan !
sumber
MS SQL Server 2012 (dan yang lebih baru) memiliki fungsi PERCENTILE_DISC yang menghitung persentil tertentu untuk nilai yang diurutkan. PERCENTILE_DISC (0,5) akan menghitung median - https://msdn.microsoft.com/en-us/library/hh231327.aspx
sumber
Sederhana, cepat, akurat
sumber
Jika Anda ingin menggunakan fungsi Buat Agregat di SQL Server, ini adalah bagaimana melakukannya. Melakukannya dengan cara ini bermanfaat untuk dapat menulis pertanyaan yang bersih. Perhatikan bahwa proses ini dapat disesuaikan untuk menghitung nilai Persentil dengan cukup mudah.
Buat proyek Visual Studio baru dan atur kerangka target menjadi .NET 3.5 (ini untuk SQL 2008, mungkin berbeda di SQL 2012). Kemudian buat file kelas dan masukkan kode berikut, atau setara dengan c #:
Kemudian kompilasi dan salin file DLL dan PDB ke mesin SQL Server Anda dan jalankan perintah berikut dalam SQL Server:
Anda kemudian dapat menulis kueri untuk menghitung median seperti ini: SELECT dbo.Median (Field) FROM Table
sumber
Saya baru saja menemukan halaman ini sambil mencari solusi berbasis set untuk median. Setelah melihat beberapa solusi di sini, saya datang dengan yang berikut ini. Harapan itu membantu / bekerja.
sumber
Kueri berikut mengembalikan median dari daftar nilai dalam satu kolom. Itu tidak dapat digunakan sebagai atau bersama dengan fungsi agregat, tetapi Anda masih dapat menggunakannya sebagai sub-kueri dengan klausa WHERE di pilih dalam.
SQL Server 2005+:
sumber
Meskipun solusi Justin grant tampak solid, saya menemukan bahwa ketika Anda memiliki sejumlah nilai duplikat di dalam kunci partisi yang diberikan, nomor baris untuk nilai duplikat ASC berakhir di luar urutan sehingga mereka tidak benar menyelaraskan.
Ini adalah bagian dari hasil saya:
Saya menggunakan kode Justin sebagai dasar untuk solusi ini. Meskipun tidak seefisien mengingat penggunaan beberapa tabel turunan, ia menyelesaikan masalah pemesanan baris yang saya temui. Setiap perbaikan akan disambut baik karena saya tidak berpengalaman dalam T-SQL.
sumber
Contoh Justin di atas sangat bagus. Tetapi kebutuhan kunci Primer itu harus dinyatakan dengan sangat jelas. Saya telah melihat kode di alam bebas tanpa kunci dan hasilnya buruk.
Keluhan yang saya dapatkan tentang Percentile_Cont adalah bahwa ia tidak akan memberi Anda nilai aktual dari dataset. Untuk mendapatkan "median" yang merupakan nilai aktual dari dataset gunakan Percentile_Disc.
sumber
Dalam UDF, tulis:
sumber
Temuan Median
Ini adalah metode paling sederhana untuk menemukan median atribut.
sumber
Lihat solusi lain untuk perhitungan median dalam SQL di sini: " Cara sederhana untuk menghitung median dengan MySQL " (solusinya kebanyakan adalah vendor-independent).
sumber
Untuk variabel kontinu / ukur 'col1' dari 'table1'
sumber
Menggunakan agregat COUNT, Anda dapat menghitung berapa banyak baris yang ada dan menyimpan dalam variabel yang disebut @cnt. Kemudian Anda dapat menghitung parameter untuk filter OFFSET-FETCH untuk menentukan, berdasarkan urutan qty, berapa banyak baris untuk dilewati (nilai offset) dan berapa banyak untuk menyaring (mengambil nilai).
Jumlah baris yang dilewati adalah (@cnt - 1) / 2. Jelas bahwa untuk hitungan ganjil perhitungan ini benar karena Anda pertama-tama mengurangi 1 untuk nilai tengah tunggal, sebelum Anda bagi dengan 2.
Ini juga berfungsi dengan benar untuk penghitungan genap karena pembagian yang digunakan dalam ekspresi adalah pembagian bilangan bulat; jadi, saat mengurangkan 1 dari hitungan genap, Anda memiliki nilai ganjil.
Ketika membagi nilai ganjil itu dengan 2, bagian fraksi dari hasil (0,5) terpotong. Jumlah baris yang akan diambil adalah 2 - (@cnt% 2). Idenya adalah ketika hitungannya ganjil, hasil operasi modulo adalah 1, dan Anda harus mengambil 1 baris. Ketika hitungan bahkan hasil operasi modulo adalah 0, dan Anda perlu mengambil 2 baris. Dengan mengurangi hasil 1 atau 0 dari operasi modulo dari 2, Anda mendapatkan masing-masing 1 atau 2 yang diinginkan. Akhirnya, untuk menghitung kuantitas median, ambil satu atau dua jumlah hasil, dan terapkan rata-rata setelah mengonversi nilai integer input ke numerik sebagai berikut:
sumber
Saya ingin mencari solusi sendiri, tetapi otak saya tersandung dan jatuh di jalan. Saya pikir itu berhasil, tetapi jangan meminta saya untuk menjelaskannya di pagi hari. : P
sumber
sumber
Ini bekerja dengan SQL 2000:
sumber
Untuk pemula seperti saya yang mempelajari dasar-dasarnya, saya pribadi menemukan contoh ini lebih mudah diikuti, karena lebih mudah untuk memahami apa yang terjadi dan dari mana nilai median berasal ...
Sangat mengagumi beberapa kode di atas !!!
sumber
Ini sesederhana jawaban yang bisa saya berikan. Bekerja dengan baik dengan data saya. Jika Anda ingin mengecualikan nilai-nilai tertentu, tambahkan saja klausa where ke inner select.
sumber
Solusi berikut berfungsi berdasarkan asumsi ini:
Kode:
sumber
sumber
Saya mencoba dengan beberapa alternatif, tetapi karena catatan data saya memiliki nilai berulang, versi ROW_NUMBER tampaknya bukan pilihan bagi saya. Jadi di sini kueri yang saya gunakan (versi dengan NTILE):
sumber
Membangun jawaban Jeff Atwood di atas di sini adalah dengan GROUP BY dan subquery yang berkorelasi untuk mendapatkan median untuk setiap grup.
sumber
Seringkali, kita mungkin perlu menghitung Median tidak hanya untuk seluruh tabel, tetapi untuk agregat sehubungan dengan beberapa ID. Dengan kata lain, hitung median untuk setiap ID di tabel kami, di mana setiap ID memiliki banyak catatan. (berdasarkan solusi yang diedit oleh @gdoron: kinerja bagus dan berfungsi di banyak SQL)
Semoga ini bisa membantu.
sumber
Untuk pertanyaan Anda, Jeff Atwood sudah memberikan solusi sederhana dan efektif. Tetapi, jika Anda mencari beberapa pendekatan alternatif untuk menghitung median, kode SQL di bawah ini akan membantu Anda.
Jika Anda ingin menghitung median di MySQL, tautan github ini akan berguna.
sumber
Ini adalah solusi paling optimal untuk menemukan median yang bisa saya pikirkan. Nama-nama dalam contoh ini didasarkan pada contoh Justin. Pastikan indeks untuk tabel Sales.SalesOrderHeader ada dengan kolom indeks CustomerId dan TotalDue dalam urutan itu.
MEMPERBARUI
Saya agak tidak yakin tentang metode mana yang memiliki kinerja terbaik, jadi saya melakukan perbandingan antara metode saya Justin Grants dan Jeff Atwoods dengan menjalankan kueri berdasarkan ketiga metode dalam satu batch dan biaya batch dari setiap kueri adalah:
Tanpa indeks:
Dan dengan indeks
Saya mencoba melihat seberapa baik skala kueri jika Anda memiliki indeks dengan membuat lebih banyak data dari sekitar 14.000 baris dengan faktor 2 hingga 512 yang berarti pada akhirnya sekitar 7,2 juta baris. Catatan saya memastikan bidang CustomeId di mana unik untuk setiap kali saya melakukan satu salinan, sehingga proporsi baris dibandingkan dengan contoh unik CustomerId tetap konstan. Ketika saya melakukan ini, saya menjalankan eksekusi di mana saya membangun kembali indeks setelah itu, dan saya perhatikan hasilnya stabil di sekitar faktor 128 dengan data yang saya miliki untuk nilai-nilai ini:
Saya bertanya-tanya bagaimana kinerja dapat dipengaruhi oleh penskalaan jumlah baris tetapi menjaga CustomerId unik konstan, jadi saya menyiapkan tes baru di mana saya melakukan ini. Sekarang alih-alih menstabilkan, rasio biaya batch terus menyimpang, juga bukannya sekitar 20 baris per CustomerId per rata-rata saya pada akhirnya sekitar 10.000 baris per Id unik tersebut. Angka-angka di mana:
Saya memastikan saya menerapkan setiap metode dengan benar dengan membandingkan hasilnya. Kesimpulan saya adalah metode yang saya gunakan umumnya lebih cepat selama indeks ada. Juga memperhatikan bahwa metode ini adalah apa yang direkomendasikan untuk masalah khusus ini dalam artikel ini https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqNum=5
Cara untuk lebih meningkatkan kinerja panggilan berikutnya ke permintaan ini lebih jauh adalah dengan tetap menggunakan informasi jumlah dalam tabel tambahan. Anda bahkan dapat mempertahankannya dengan memiliki pemicu yang memutakhirkan dan menyimpan informasi mengenai jumlah baris SalesOrderHeader tergantung pada CustomerId, tentu saja Anda kemudian dapat menyimpan median juga.
sumber
Untuk dataset skala besar, Anda dapat mencoba GIST ini:
https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2
Ia bekerja dengan menggabungkan nilai-nilai berbeda yang akan Anda temukan di set Anda (seperti usia, atau tahun kelahiran, dll.), Dan menggunakan fungsi-fungsi jendela SQL untuk menemukan posisi persentil yang Anda tentukan dalam kueri.
sumber