Ini adalah masalah yang saya habiskan berjam-jam meneliti di masa lalu. Tampaknya bagi saya adalah sesuatu yang seharusnya ditangani oleh solusi RDBMS modern tetapi sampai sekarang saya belum menemukan apa pun yang benar-benar menjawab apa yang saya lihat sebagai kebutuhan yang sangat umum dalam aplikasi Web atau Windows dengan basis data back-end.
Saya berbicara tentang penyortiran dinamis. Di dunia fantasi saya, itu harus sesederhana sesuatu seperti:
ORDER BY @sortCol1, @sortCol2
Ini adalah contoh kanonik yang diberikan oleh pengembang SQL dan Stored Procedure pemula di seluruh forum di Internet. "Mengapa ini tidak mungkin?" mereka bertanya. Selalu, seseorang akhirnya datang untuk memberi tahu mereka tentang sifat kompilasi dari prosedur tersimpan, rencana eksekusi secara umum, dan segala macam alasan lain mengapa tidak mungkin untuk menempatkan parameter secara langsung ke dalam ORDER BY
klausa.
Saya tahu apa yang sudah Anda pikirkan: "Biarkan klien melakukan penyortiran, kalau begitu." Secara alami, ini membongkar pekerjaan dari basis data Anda. Dalam kasus kami, server database kami bahkan tidak berkeringat 99% dari waktu dan mereka bahkan belum multi-core atau salah satu dari banyak sekali peningkatan lainnya untuk arsitektur sistem yang terjadi setiap 6 bulan. Untuk alasan ini saja, memiliki database kami menangani penyortiran tidak akan menjadi masalah. Selain itu, basis data sangatpandai menyortir. Mereka dioptimalkan untuk itu dan telah bertahun-tahun untuk melakukannya dengan benar, bahasa untuk melakukannya sangat fleksibel, intuitif, dan sederhana dan di atas semua penulis SQL pemula tahu bagaimana melakukannya dan bahkan lebih penting lagi mereka tahu cara mengeditnya, membuat perubahan, melakukan pemeliharaan, dll. Ketika database Anda jauh dari pajak dan Anda hanya ingin menyederhanakan (dan mempersingkat!) waktu pengembangan ini sepertinya pilihan yang jelas.
Lalu ada masalah web. Saya telah bermain-main dengan JavaScript yang akan melakukan pemilahan tabel HTML sisi klien, tetapi mereka pasti tidak cukup fleksibel untuk kebutuhan saya dan, sekali lagi, karena basis data saya tidak dikenai pajak dan dapat melakukan pengurutan dengan sangat mudah, saya mengalami kesulitan membenarkan waktu yang diperlukan untuk menulis ulang atau menyortir penyortir JavaScript saya sendiri. Hal yang sama berlaku untuk penyortiran sisi server, meskipun mungkin sudah lebih disukai daripada JavaScript. Saya bukan orang yang suka overhead DataSets, jadi tuntut saya.
Tetapi ini membawa kembali poin bahwa itu tidak mungkin - atau lebih tepatnya, tidak mudah. Saya telah melakukan, dengan sistem sebelumnya, cara hack yang luar biasa untuk mendapatkan penyortiran dinamis. Itu tidak cantik, tidak intuitif, sederhana, atau fleksibel dan penulis SQL pemula akan hilang dalam hitungan detik. Sudah mencari bukan "solusi" tapi "komplikasi."
Contoh-contoh berikut ini tidak dimaksudkan untuk mengekspos segala praktik terbaik atau gaya pengkodean yang baik atau apa pun, juga tidak menunjukkan kemampuan saya sebagai programmer T-SQL. Mereka adalah apa adanya dan saya sepenuhnya mengakui mereka membingungkan, bentuk buruk, dan sekadar peretasan.
Kami meneruskan nilai integer sebagai parameter ke prosedur tersimpan (sebut saja parameter "sort") dan dari situ kami menentukan banyak variabel lain. Misalnya ... katakanlah sort adalah 1 (atau default):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
Anda sudah dapat melihat bagaimana jika saya mendeklarasikan lebih banyak variabel @colX untuk mendefinisikan kolom lain, saya benar-benar bisa berkreasi dengan kolom untuk mengurutkan berdasarkan nilai "sort" ... untuk menggunakannya, biasanya berakhir seperti berikut ini. klausa yang sangat berantakan:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
Jelas ini adalah contoh yang sangat sederhana. Hal-hal nyata, karena kami biasanya memiliki empat atau lima kolom untuk mendukung penyortiran, masing-masing dengan kemungkinan kolom kedua atau bahkan ketiga untuk disortir di samping itu (misalnya tanggal turun lalu disortir secara sekunder dengan nama naik) dan masing-masing mendukung dua pengurutan terarah yang secara efektif menggandakan jumlah kasus. Ya ... rambut itu menjadi sangat cepat.
Idenya adalah bahwa seseorang dapat "dengan mudah" mengubah kasus semacam itu sehingga vehicleid bisa disortir sebelum masa penyimpanan ... tetapi fleksibilitas semu, setidaknya dalam contoh sederhana ini, benar-benar berakhir di sana. Pada dasarnya, setiap kasus yang gagal tes (karena metode pengurutan kami tidak berlaku untuk kali ini) memberikan nilai NULL. Dan akhirnya Anda memiliki klausa yang berfungsi seperti berikut:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Anda mendapatkan idenya. Ini berfungsi karena SQL Server secara efektif mengabaikan nilai null dalam urutan klausa. Ini sangat sulit untuk dipertahankan, karena siapa pun yang memiliki pengetahuan dasar SQL mungkin bisa melihatnya. Jika saya kehilangan salah satu dari Anda, jangan merasa buruk. Kami butuh waktu lama untuk membuatnya berfungsi dan kami masih bingung mencoba mengeditnya atau membuat yang baru seperti itu. Untungnya itu tidak perlu sering berubah, kalau tidak cepat akan menjadi "tidak sepadan dengan masalah."
Namun melakukan pekerjaan.
Pertanyaan saya kemudian: apakah ada cara yang lebih baik?
Saya baik-baik saja dengan solusi selain yang Disimpan Prosedur, karena saya menyadari itu mungkin bukan jalan untuk pergi. Lebih disukai, saya ingin tahu apakah ada yang bisa melakukannya dengan lebih baik dalam Prosedur Tersimpan, tetapi jika tidak, bagaimana Anda semua menangani membiarkan pengguna secara dinamis mengurutkan tabel data (dua arah juga) dengan ASP.NET?
Dan terima kasih telah membaca (atau setidaknya membaca sepintas lalu) pertanyaan panjang!
PS: Senang saya tidak menunjukkan contoh saya tentang prosedur tersimpan yang mendukung penyortiran dinamis, penyaringan dinamis / pencarian teks kolom, pagination melalui ROWNUMBER () LEBIH, DAN coba ... tangkap dengan transaksi rollbacking pada kesalahan ... "ukuran raksasa" bahkan tidak mulai menggambarkannya.
Memperbarui:
- Saya ingin menghindari SQL dinamis . Parsing string bersama-sama dan menjalankan EXEC di atasnya mengalahkan banyak tujuan memiliki prosedur tersimpan di tempat pertama. Kadang-kadang saya bertanya-tanya apakah jika melakukan hal seperti itu tidak akan sepadan, setidaknya dalam kasus penyortiran dinamis khusus ini. Namun, saya selalu merasa kotor setiap kali saya melakukan string SQL dinamis seperti itu - seperti saya masih hidup di dunia ASP Klasik.
- Banyak alasan kami ingin prosedur tersimpan di tempat pertama adalah untuk keamanan . Saya tidak bisa membuat panggilan pada masalah keamanan, hanya menyarankan solusi. Dengan SQL Server 2005 kita dapat mengatur izin (berdasarkan per pengguna jika perlu) di tingkat skema pada masing-masing prosedur yang tersimpan dan kemudian menolak setiap pertanyaan terhadap tabel secara langsung. Mengkritik pro dan kontra dari pendekatan ini mungkin untuk pertanyaan lain, tetapi sekali lagi itu bukan keputusan saya. Saya hanya monyet kode utama. :)
sumber
Jawaban:
Ya, itu menyebalkan, dan cara Anda melakukannya mirip dengan apa yang saya lakukan:
Bagi saya, ini masih jauh lebih baik daripada membangun SQL dinamis dari kode, yang berubah menjadi skalabilitas dan pemeliharaan mimpi buruk untuk DBA.
Apa yang saya lakukan dari kode adalah refactor halaman dan penyortiran jadi saya setidaknya tidak memiliki banyak pengulangan di sana dengan mengisi nilai untuk
@SortExpr
dan@SortDir
.Sejauh menyangkut SQL, pertahankan desain dan format yang sama antara berbagai prosedur tersimpan, jadi setidaknya rapi dan dapat dikenali saat Anda masuk untuk membuat perubahan.
sumber
Pendekatan ini menjaga kolom yang dapat disortir dari digandakan dua kali dalam urutan oleh, dan sedikit lebih mudah dibaca IMO:
sumber
SQL dinamis masih merupakan opsi. Anda hanya perlu memutuskan apakah opsi itu lebih enak dari apa yang Anda miliki saat ini.
Berikut ini adalah artikel yang menunjukkan bahwa: http://www.4guysfromrolla.com/webtech/010704-1.shtml .
sumber
Aplikasi saya banyak melakukan ini tetapi mereka semua secara dinamis membangun SQL. Namun, ketika saya berurusan dengan prosedur tersimpan saya melakukan ini:
select * from dbo.fn_myData() where ... order by ...
sehingga Anda dapat menentukan urutan sortir secara dinamis di sana.Maka setidaknya bagian dinamis ada di aplikasi Anda, tetapi database masih melakukan tugas berat.
sumber
Teknik prosedur tersimpan (hack?) Saya telah menggunakan untuk menghindari SQL dinamis untuk pekerjaan tertentu adalah memiliki kolom semacam unik. Yaitu,
Yang satu ini mudah dikalahkan dalam pengiriman - Anda dapat menggabungkan bidang di kolom mySort Anda, membalik urutan dengan fungsi matematika atau tanggal, dll
Lebih disukai, saya menggunakan tampilan jaringan asp.net saya atau objek lain dengan penyortiran bawaan untuk melakukan penyortiran bagi saya SETELAH mengambil data dari Sql-Server. Atau bahkan jika itu bukan built-in - misalnya, datatables, dll di asp.net.
sumber
Ada beberapa cara berbeda untuk meretas ini.
Prasyarat:
Kemudian masukkan ke tabel temp:
Metode # 2: mengatur server tertaut kembali ke dirinya sendiri, lalu pilih dari ini menggunakan openquery: http://www.sommarskog.se/share_data.html#OPENQUERY
sumber
Mungkin ada opsi ketiga, karena server Anda memiliki banyak siklus cadangan - gunakan prosedur pembantu untuk melakukan penyortiran melalui tabel sementara. Sesuatu seperti
Peringatan: Saya belum menguji ini, tetapi "harus" bekerja di SQL Server 2005 (yang akan membuat tabel sementara dari hasil yang ditetapkan tanpa menentukan kolom di muka.)
sumber
Pada titik tertentu, tidakkah menjadi layak untuk beralih dari prosedur tersimpan dan hanya menggunakan kueri yang diparameterisasi untuk menghindari peretasan semacam ini?
sumber
Saya setuju, gunakan sisi klien. Tapi sepertinya itu bukan jawaban yang ingin Anda dengar.
Jadi, itu sempurna sebagaimana adanya. Saya tidak tahu mengapa Anda ingin mengubahnya, atau bahkan bertanya, "Apakah ada cara yang lebih baik." Sungguh, itu harus disebut "Jalan". Selain itu, tampaknya berfungsi dan sesuai dengan kebutuhan proyek dengan baik dan mungkin akan cukup panjang untuk tahun-tahun mendatang. Karena basis data Anda tidak dikenai pajak dan pengurutan benar-benar mudah, itu harus tetap seperti itu selama bertahun-tahun yang akan datang.
Saya tidak akan berkeringat.
sumber
Ketika Anda paging hasil diurutkan, SQL dinamis adalah pilihan yang baik. Jika Anda paranoid tentang injeksi SQL, Anda dapat menggunakan nomor kolom alih-alih nama kolom. Saya sudah melakukan ini sebelum menggunakan nilai negatif untuk turun. Sesuatu seperti ini...
Maka Anda hanya perlu memastikan nomornya ada di dalam 1 hingga # kolom. Anda bahkan dapat memperluas ini ke daftar nomor kolom dan menguraikannya ke dalam tabel int menggunakan fungsi seperti ini . Maka Anda akan membangun pesanan dengan klausa seperti ...
Salah satu kelemahannya adalah Anda harus mengingat urutan setiap kolom di sisi klien. Terutama, ketika Anda tidak menampilkan semua kolom atau Anda menampilkannya dalam urutan yang berbeda. Ketika klien ingin menyortir, Anda memetakan nama kolom ke urutan kolom dan menghasilkan daftar int.
sumber
Argumen yang menentang melakukan pengurutan di sisi klien adalah data volume besar dan pagination. Setelah jumlah baris melampaui apa yang dapat Anda tampilkan dengan mudah, Anda sering menyortir sebagai bagian dari lompatan / ambil, yang mungkin ingin Anda jalankan dalam SQL.
Untuk Kerangka Entity, Anda bisa menggunakan prosedur tersimpan untuk menangani pencarian teks Anda. Jika Anda mengalami masalah pengurutan yang sama, solusi yang saya lihat adalah menggunakan proc yang disimpan untuk pencarian, hanya mengembalikan kunci id yang ditetapkan untuk pertandingan. Selanjutnya, permintaan ulang (dengan pengurutan) terhadap db menggunakan id dalam daftar (berisi). EF menangani ini dengan cukup baik, bahkan ketika set ID cukup besar. Ya, ini adalah dua perjalanan bolak-balik, tetapi memungkinkan Anda untuk selalu menyortir dalam DB, yang dapat menjadi penting dalam beberapa situasi, dan mencegah Anda dari menulis muatan logika dalam prosedur yang tersimpan.
sumber
Bagaimana dengan penanganan pengurutan pada hal-hal yang menampilkan hasil - kisi, laporan, dll, bukan pada SQL?
EDIT:
Untuk memperjelas hal-hal sejak jawaban ini dipilih sebelumnya, saya akan menguraikan sedikit ...
Anda menyatakan Anda tahu tentang penyortiran sisi klien tetapi ingin menghindari itu. Itu panggilan Anda, tentu saja.
Apa yang ingin saya tunjukkan adalah dengan melakukannya di sisi klien, Anda dapat menarik data SEKALI dan kemudian bekerja dengannya sesuka Anda - dibandingkan melakukan beberapa perjalanan bolak-balik ke server setiap kali semacam itu berubah.
SQL Server Anda tidak dikenakan pajak sekarang dan itu luar biasa. Seharusnya tidak. Tetapi hanya karena itu tidak kelebihan beban namun tidak berarti bahwa itu akan tetap seperti itu selamanya.
Jika Anda menggunakan salah satu dari hal-hal ASP.NET yang lebih baru untuk ditampilkan di web, banyak hal yang sudah dipanggang.
Apakah perlu menambahkan begitu banyak kode ke setiap prosedur tersimpan hanya untuk menangani penyortiran? Sekali lagi, teleponmu.
Saya bukan orang yang pada akhirnya akan bertanggung jawab untuk mendukungnya. Tetapi pikirkan apa yang akan terlibat ketika kolom ditambahkan / dihapus dalam berbagai dataset yang digunakan oleh prosedur tersimpan (membutuhkan modifikasi pada pernyataan KASUS) atau ketika tiba-tiba alih-alih menyortir oleh dua kolom, pengguna memutuskan mereka membutuhkan tiga - mengharuskan Anda untuk memperbarui semua prosedur tersimpan Anda yang menggunakan metode ini.
Bagi saya, itu layak untuk mendapatkan solusi sisi klien yang berfungsi dan menerapkannya pada beberapa tampilan data yang dihadapi pengguna dan dilakukan dengan itu. Jika kolom baru ditambahkan, itu sudah ditangani. Jika pengguna ingin mengurutkan berdasarkan beberapa kolom, mereka dapat mengurutkannya dengan dua atau dua puluh kolom.
sumber
Maaf saya terlambat ke pesta, tapi di sini ada pilihan lain bagi mereka yang benar-benar ingin menghindari SQL dinamis, tetapi menginginkan fleksibilitas yang ditawarkannya:
Alih-alih secara dinamis menghasilkan SQL on the fly, tulis kode untuk menghasilkan proc unik untuk setiap variasi yang mungkin. Kemudian Anda dapat menulis metode dalam kode untuk melihat opsi pencarian dan memilihnya untuk memanggil proc.
Jika Anda hanya memiliki beberapa variasi maka Anda cukup membuat procs dengan tangan. Tetapi jika Anda memiliki banyak variasi maka alih-alih harus mempertahankan semuanya, Anda hanya perlu mempertahankan generator proc Anda untuk membuatnya membuatnya kembali.
Sebagai manfaat tambahan, Anda akan mendapatkan paket SQL yang lebih baik untuk kinerja yang lebih baik melakukannya dengan cara ini juga.
sumber
Solusi ini mungkin hanya bekerja di .NET, saya tidak tahu.
Saya mengambil data ke dalam C # dengan urutan pengurutan awal dalam urutan SQL oleh klausa, memasukkan data itu ke dalam DataView, menyimpannya dalam variabel Sesi, dan menggunakannya untuk membangun halaman.
Ketika pengguna mengklik pada judul kolom untuk mengurutkan (atau halaman, atau filter), saya tidak kembali ke database. Sebagai gantinya, saya kembali ke DataView yang di-cache dan mengatur properti "Sort" ke ekspresi yang saya buat secara dinamis, sama seperti saya akan dinamis SQL. (Saya melakukan penyaringan dengan cara yang sama, menggunakan properti "RowFilter").
Anda dapat melihat / merasakannya berfungsi dalam demo aplikasi saya, BugTracker.NET, di http://ifdefined.com/btnet/bugs.aspx
sumber
Anda harus menghindari penyortiran SQL Server, kecuali jika perlu. Mengapa tidak mengurutkan pada server aplikasi atau sisi klien? Juga .NET Generics melakukan sortin yang luar biasa
sumber