Kita perlu melakukan beberapa pelaporan pada nilai-nilai yang biasanya dicampur string angka dan huruf yang perlu diurutkan 'secara alami'. Hal-hal seperti, misalnya "P7B18" atau "P12B3". @ String sebagian besar akan menjadi urutan huruf kemudian angka bergantian. Namun, jumlah segmen ini dan panjang masing-masing dapat bervariasi.
Kami ingin bagian numerik ini diurutkan dalam urutan numerik. Jelas, jika saya hanya menangani nilai-nilai string secara langsung dengan ORDER BY
, maka "P12B3" akan datang sebelum "P7B18", karena "P1" lebih awal dari "P7", tetapi saya ingin sebaliknya, karena "P7" secara alami mendahului "P12".
Saya juga ingin dapat melakukan perbandingan jarak, misalnya @bin < 'P13S6'
atau semacamnya. Saya tidak harus menangani floating point atau angka negatif; ini akan menjadi bilangan bulat non-negatif yang kita hadapi. Panjang string dan jumlah segmen berpotensi arbitrer, tanpa batas atas yang tetap.
Dalam kasus kami, casing string tidak penting, meskipun jika ada cara untuk melakukan ini dengan cara sadar-collation, orang lain mungkin menemukan itu berguna. Bagian terburuk dari semua ini adalah saya ingin dapat melakukan keduanya memesan, dan berbagai penyaringan dalam WHERE
klausa.
Jika saya melakukan ini dalam C #, itu akan menjadi tugas yang cukup sederhana: melakukan parsing untuk memisahkan alfa dari angka, mengimplementasikan IComparable, dan pada dasarnya Anda sudah selesai. SQL Server, tentu saja, tampaknya tidak menawarkan fungsionalitas serupa, setidaknya sejauh yang saya ketahui.
Adakah yang tahu ada trik bagus untuk membuat ini bekerja? Apakah ada sedikit kemampuan yang dipublikasikan untuk membuat tipe CLR khusus yang mengimplementasikan IComparable dan melakukan hal ini seperti yang diharapkan? Saya juga tidak menentang Trik XML Bodoh (lihat juga: daftar concatenation), dan saya punya fungsi pencocokan / ekstraksi / penggantian pembungkus CLR yang tersedia di server juga.
EDIT: Sebagai contoh yang sedikit lebih rinci, saya ingin data berperilaku seperti ini.
SELECT bin FROM bins ORDER BY bin
bin
--------------------
M7R16L
P8RF6JJ
P16B5
PR7S19
PR7S19L
S2F3
S12F0
yaitu memecah string menjadi token semua huruf atau semua angka, dan mengurutkannya baik secara alfabet atau numerik, dengan token paling kiri menjadi istilah pengurutan yang paling signifikan. Seperti yang saya sebutkan, sepotong kue di. NET jika Anda mengimplementasikan IComparable, tapi saya tidak tahu bagaimana (atau jika) Anda dapat melakukan hal semacam itu di SQL Server. Ini jelas bukan sesuatu yang pernah saya temui dalam 10 tahun atau lebih bekerja dengannya.
P7B12
bisa menjadiP 07 B 12
, lalu (via ASCII)80 07 65 12
, jadi80076512
Jawaban:
Ingin cara yang masuk akal, efisien menyortir angka dalam string sebagai angka aktual? Pertimbangkan memilih untuk saran Microsoft Connect saya: Mendukung "penyortiran alami" / DIGITSASNUMBERS sebagai opsi Kolasi
Tidak ada cara yang mudah dan terintegrasi untuk melakukan ini, tetapi ada kemungkinan:
Normalisasikan string dengan memformatnya kembali ke segmen dengan panjang tetap:
VARCHAR(50) COLLATE Latin1_General_100_BIN2
. Panjang maksimal 50 mungkin perlu disesuaikan berdasarkan jumlah segmen maksimum dan panjang maksimum potensial mereka.AFTER [or FOR] INSERT, UPDATE
Pemicu sehingga Anda dijamin dengan benar mengatur nilai untuk semua catatan, bahkan mereka datang melalui permintaan ad hoc, dll. Tentu saja, UDF skalar itu juga dapat ditangani melalui SQLCLR, tetapi perlu diuji untuk menentukan mana yang sebenarnya lebih efisien. **UPPER()
fungsi pada hasil akhir dari semua segmen (sehingga hanya perlu dilakukan sekali dan tidak per segmen). Ini akan memungkinkan penyortiran yang tepat mengingat susunan biner dari kolom sortir.AFTER INSERT, UPDATE
Pemicu pada tabel yang memanggil UDF untuk mengatur kolom sortir. Untuk meningkatkan kinerja, gunakanUPDATE()
fungsi untuk menentukan apakah kolom kode ini bahkan dalamSET
klausaUPDATE
pernyataan (hanyaRETURN
jika salah), dan kemudian bergabung denganINSERTED
danDELETED
tabel pseudo pada kolom kode untuk hanya memproses baris yang memiliki perubahan dalam nilai kode . Pastikan untuk menentukanCOLLATE Latin1_General_100_BIN2
kondisi GABUNG tersebut untuk memastikan keakuratan dalam menentukan apakah ada perubahan.Contoh:
Dalam pendekatan ini, Anda dapat mengurutkan melalui:
Dan Anda dapat melakukan pemfilteran rentang melalui:
atau:
Baik
ORDER BY
danWHERE
filter harus menggunakan pemeriksaan biner yang ditentukanSortColumn
karena Colled Precedence.Perbandingan kesetaraan masih akan dilakukan pada kolom nilai asli.
Pikiran lain:
Gunakan SQLCLR UDT. Ini mungkin bisa berhasil, meskipun tidak jelas apakah ini menyajikan keuntungan bersih dibandingkan dengan pendekatan yang dijelaskan di atas.
Ya, SQLCLR UDT dapat membuat operator pembandingnya ditimpa dengan algoritme khusus. Ini menangani situasi di mana nilainya dibandingkan dengan nilai lain yang sudah jenis kustom yang sama, atau yang perlu dikonversi secara implisit. Ini harus menangani filter rentang dalam
WHERE
kondisi.Berkenaan dengan mengurutkan UDT sebagai jenis kolom biasa (bukan kolom yang dihitung), ini hanya mungkin jika UDT adalah "byte dipesan". Menjadi "byte dipesan" berarti bahwa representasi biner dari UDT (yang dapat didefinisikan dalam UDT) secara alami mengurutkan dalam urutan yang sesuai. Dengan asumsi bahwa representasi biner ditangani mirip dengan pendekatan yang dijelaskan di atas untuk kolom VARCHAR (50) yang memiliki segmen panjang tetap yang diisi, yang akan memenuhi syarat. Atau, jika tidak mudah untuk memastikan bahwa representasi biner secara alami akan dipesan dengan cara yang tepat, Anda bisa mengekspos metode atau properti UDT yang menghasilkan nilai yang akan dipesan dengan benar, dan kemudian membuat
PERSISTED
kolom yang dikomputasi pada metode atau properti. Metode harus deterministik dan ditandai sebagaiIsDeterministic = true
.Manfaat dari pendekatan ini adalah:
Parse
metode UDT mengambilP7B18
nilai dan mengubahnya, maka Anda harus dapat dengan mudah memasukkan nilai-nilai secara alamiP7B18
. Dan dengan metode konversi implisit yang diatur dalam UDT, kondisi WHERE juga memungkinkan untuk menggunakan P7B18` saja.Konsekuensi dari pendekatan ini adalah:
PERSISTED
kolom yang dihitung pada properti atau metode UDT, maka Anda akan mendapatkan representasi dikembalikan oleh properti atau metode. Jika Anda menginginkan nilai asliP7B18
, maka Anda perlu memanggil metode atau properti UDT yang dikodekan untuk mengembalikan representasi itu. Karena Anda tetap harus menggantiToString
metode, itu adalah kandidat yang baik untuk menyediakan ini.Tidak jelas (setidaknya bagi saya saat ini karena saya belum menguji bagian ini) seberapa mudah / sulit akan membuat perubahan pada representasi biner. Mengubah representasi yang tersimpan dan dapat disortir mungkin perlu dijatuhkan dan ditambahkan kembali bidang. Juga, menjatuhkan Majelis yang berisi UDT akan gagal jika digunakan dengan cara apa pun, jadi Anda ingin memastikan bahwa tidak ada yang lain di Majelis selain UDT ini. Anda dapat
ALTER ASSEMBLY
mengganti definisi, tetapi ada beberapa batasan untuk itu.Di sisi lain,
VARCHAR()
bidang adalah data yang terputus dari algoritma sehingga hanya perlu memperbarui kolom. Dan jika ada puluhan juta baris (atau lebih) maka itu dapat dilakukan dalam pendekatan batch.Terapkan perpustakaan ICU yang sebenarnya memungkinkan untuk melakukan pengurutan alfanumerik ini. Meskipun sangat fungsional, perpustakaan hanya tersedia dalam dua bahasa: C / C ++ dan Java. Yang berarti Anda mungkin perlu melakukan beberapa tweak untuk membuatnya berfungsi di Visual C ++, atau ada kemungkinan kode Java dapat dikonversi ke MSIL menggunakan IKVM . Ada satu atau dua proyek sampingan .NET yang ditautkan di situs itu yang menyediakan antarmuka COM yang dapat diakses dalam kode yang dikelola, tetapi saya percaya mereka belum diperbarui dalam beberapa saat dan saya belum mencobanya. Taruhan terbaik di sini adalah menangani ini di lapisan aplikasi dengan tujuan menghasilkan kunci sortir. Kunci sortir kemudian akan disimpan ke dalam kolom sortir baru.
Ini mungkin bukan pendekatan yang paling praktis. Namun, masih sangat keren bahwa kemampuan seperti itu ada. Saya memberikan walk-through yang lebih rinci dari contoh ini dalam jawaban berikut:
Apakah ada susunan untuk menyortir string berikut dalam urutan berikut 1,2,3,6,10,10A, 10B, 11?
Tetapi pola yang dibahas dalam pertanyaan itu sedikit lebih sederhana. Untuk contoh yang menunjukkan bahwa jenis pola yang ditangani dalam Pertanyaan ini juga berfungsi, silakan kunjungi halaman berikut:
Demo Pengumpulan ICU
Di bawah "Pengaturan", atur opsi "numerik" ke "pada" dan semua yang lain harus diatur ke "default". Selanjutnya, di sebelah kanan tombol "sortir", hapus centang opsi untuk "kekuatan diff" dan centang opsi untuk "kunci sortir". Kemudian ganti daftar item dalam area teks "Input" dengan daftar berikut:
Klik tombol "sortir". Area teks "Output" akan menampilkan yang berikut:
Harap dicatat bahwa kunci pengurutan adalah struktur dalam beberapa bidang, dipisahkan oleh koma. Setiap bidang perlu diurutkan secara independen, sehingga menyajikan masalah kecil lain untuk dipecahkan jika perlu mengimplementasikan ini dalam SQL Server.
** Jika ada kekhawatiran tentang kinerja terkait penggunaan Fungsi Buatan Pengguna, harap dicatat bahwa pendekatan yang diusulkan memanfaatkannya secara minimal. Bahkan, alasan utama untuk menyimpan nilai yang dinormalisasi adalah untuk menghindari memanggil UDF per setiap baris setiap permintaan. Dalam pendekatan utama, UDF digunakan untuk mengatur nilai
SortColumn
, dan itu hanya dilakukan padaINSERT
danUPDATE
melalui Pemicu. Memilih nilai jauh lebih umum daripada menyisipkan dan memperbarui, dan beberapa nilai tidak pernah diperbarui. Untuk setiapSELECT
kueri yang menggunakanSortColumn
filter rentang untuk dalamWHERE
klausa, UDF hanya diperlukan satu kali per masing-masing nilai range_start dan range_end untuk mendapatkan nilai yang dinormalisasi; UDF tidak disebut per-baris.Sehubungan dengan UDT, penggunaannya sebenarnya sama dengan UDF skalar. Berarti, memasukkan dan memperbarui akan memanggil metode normalisasi sekali per setiap baris untuk menetapkan nilai. Kemudian, metode normalisasi akan dipanggil sekali per kueri per setiap range_start dan range_value dalam filter rentang, tetapi tidak per baris.
Suatu titik yang mendukung penanganan normalisasi sepenuhnya dalam SQLCLR UDF adalah bahwa mengingat tidak melakukan akses data apa pun dan bersifat deterministik, jika ditandai sebagai
IsDeterministic = true
, maka ia dapat berpartisipasi dalam rencana paralel (yang mungkin membantuINSERT
danUPDATE
beroperasi) sedangkan T-SQL UDF akan mencegah rencana paralel digunakan.sumber