Apakah ada susunan untuk menyortir string berikut dalam urutan berikut 1,2,3,6,10,10A, 10B, 11?

12

Saya memiliki database dengan kolom VARCHAR yang berisi bilangan bulat dengan panjang yang bervariasi. Saya ingin mengurutkannya sehingga 10 muncul setelah 9, bukan 1, dan 70A muncul setelah 70. Saya bisa melakukan ini dengan PATINDEX () , CTE, dan pernyataan KASUS dalam klausa WHERE.

Namun, saya bertanya-tanya apakah ada susunan di mana ini tidak perlu.

Justin Dearing
sumber
Berikut adalah tautan baru untuk saran itu sekarang bahwa Microsoft telah bermigrasi dari Connect ke UserVoice namun tidak meneruskan URI: Mendukung "pengurutan alami" / DIGITSASNUMBERS sebagai opsi Kolasi
Solomon Rutzky
2
Microsoft telah mengatakan bahwa mereka akan mengimplementasikan ini sebagai fitur bawaan di SQL Server jika mereka mendapatkan suara yang cukup. Jadi pergi ke sini dan klik tombol pilih .
Peter Aylett

Jawaban:

8

Tidak. Kolasi adalah tentang penyortiran alfabet, tergantung pada halaman kode, aksen, huruf besar-kecil, kana. Karakter angka (0-9) tidak memiliki properti di sana.

Jadi 9selalu setelah 10Bdi setiap jenis.

Anda harus membaginya seperti yang Anda perhatikan atau urutkan seperti ini:

ORDER BY
    RIGHT('                              ' + MyColumn, 30)

Panjang di sebelah kanan menentukan berapa banyak ruang yang Anda miliki.

Anda tentu saja dapat:

  • memiliki 2 kolom untuk membuat ini tidak perlu (dan jauh lebih cepat) dan memiliki kolom yang dikomputasi untuk menggabungkannya
  • bersikeras memimpin nol
  • benar dibenarkan dalam char (versi tersimpan HAK saya di atas)

2 saran terakhir seperti HAK saya di atas dan sedikit berbeda. Lebih cepat untuk menyortir (tidak diperlukan pemrosesan colukmn) tetapi lebih banyak penyimpanan diperlukan

gbn
sumber
saya tidak melihat cara kerjanya. Itu rusak untuk 2, 2a, 3, dll ...
Mladen Prajdic
@Mladen Prajdic: Anda benar, oops. Lupa tentang abjad yang tertinggal
gbn
Mengenai " Jadi 9selalu setelah 10Bapapun. ": Itu hanya seperti itu di SQL Server karena opsi pengurutan yang mendasarinya untuk menangani "DigitsAsNumbers" belum diekspos sebagai opsi Collation. Namun ;-). Ini menjadi tersedia untuk aplikasi berbasis Windows dimulai pada Windows 7, terutama di File Explorer. Dan suatu hari dapat diekspos ke SQL Server, jika cukup banyak orang mendukung gagasan itu. Saya mencoba membuat bola bergulir dengan mengajukan saran Sambung berikut: Dukung "natural sorting" / DIGITSASNUMBERS sebagai opsi Collation .
Solomon Rutzky
8

Saya akan menyiapkan kolom yang dihitung kemudian mengurutkan berdasarkan itu. Sesuatu seperti

CAST( 
     CASE WHEN IS_NUMERIC(left(OtherColumn, 2) = 1) then 
         left(OtherColumn,2) 
     else 
         left(otherColumn, 1)  
AS INT)

Kemudian gunakan kolom ini untuk mengurutkan berdasarkan sekarang Anda dapat mengindeks kolom.

mrdenny
sumber
Itu sangat berguna untuk mengetahui tentang masalah yang sama. Namun, dalam hal ini saya tidak dapat mengubah skema.
Justin Dearing
Bisakah Anda menambah skema? Kecuali kolom yang dihitung, Anda selalu dapat membuat tampilan - meskipun ini tidak akan benar-benar optimal seperti kolom yang dihitung.
Aaron Bertrand
Jika Anda melakukan tampilan yang diindeks, dan memiliki edisi Perusahaan kueri Anda akan menggunakan tampilan yang diindeks secara otomatis jika dapat mengetahui apa yang Anda coba lakukan. Jika edisi standar Anda harus menggunakan WITH (NOEXPAND) untuk memicu tampilan indeks untuk digunakan. Pada titik itu Anda harus memiliki pernyataan kasus dalam pesanan Anda dengan tetapi itu harus bekerja, saya pikir.
mrdenny
Anda tidak perlu membuat kolom yang dihitung. Anda dapat menggunakan ungkapan itu langsung di klausa ORDER BY
a_horse_with_no_name
Jika Anda ingin menjamin pemindaian indeks atau tabel yakin Anda bisa. Jika Anda ingin dapat mengindeks nilai maka kolom dihitung atau tampilan diindeks diperlukan.
mrdenny
5

Jika Anda ingin cara yang menyakitkan untuk membuktikan apa yang dikatakan @gbn (pada dasarnya Anda tidak bisa memberi tahu susunan untuk memesan substring secara berbeda), Anda bisa membuat tabel #temp cepat yang memiliki koefisien untuk pesanan yang Anda harapkan, dan melihat apakah pemesanan dengan pemeriksaan apa pun mengembalikan pesanan yang sama:

CREATE TABLE #foo(id INT, n NVARCHAR(10));

CREATE TABLE #bar(collation SYSNAME);

SET NOCOUNT ON;

INSERT #foo SELECT 1,'1'
UNION SELECT 2,'2'
UNION SELECT 3,'3'
UNION SELECT 4,'6'
UNION SELECT 5,'10'
UNION SELECT 6,'10A'
UNION SELECT 7,'10B'
UNION SELECT 8,'11';

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'
    WITH x AS 
    (
        SELECT n, rn = ROW_NUMBER() OVER 
        (ORDER BY n COLLATE ' + name + ') FROM #foo
    ) 
    INSERT #bar 
    SELECT TOP (1) ''' + name + ''' FROM x
    WHERE NOT EXISTS
    (
        SELECT COUNT(*) FROM #foo AS f
        WHERE f.id = x.rn
        AND f.n <> x.n
    );' FROM sys.fn_helpcollations();

EXEC sp_executesql @sql;

SELECT collation FROM #bar;

GO
DROP TABLE #foo, #bar;

Ini berjalan untuk saya dalam waktu sekitar 10 detik dan menghasilkan 0 baris - artinya tidak ada pemeriksaan yang tersedia untuk SQL Server (setidaknya 2008 R2, belum mencoba Denali) akan mengurutkan dengan cara yang Anda harapkan. Anda perlu cara berbeda untuk mendefinisikan penyortiran.

Aaron Bertrand
sumber
2

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


Sementara Pertanyaan ini khusus untuk SQL Server, dan Jawaban ini tidak, saya merasa bahwa saya masih harus memposting informasi ini hanya untuk meningkatkan kesadaran akan hal itu dan tidak bertentangan dengan jawaban yang lain.

Yang sedang berkata, di luar SQL Server, di lingkungan tertentu adalah mungkin untuk melakukan penyortiran jenis ini. Ini adalah sesuatu yang setidaknya ditentukan dalam dokumentasi Unicode. Dalam BAHASA MARKUP DATA MARKUP DATA UNICODE (LDML) BAGIAN 5: standar / laporan COLLATION , ada bagan untuk Pengaturan Collation yang menjelaskan berbagai opsi untuk menyesuaikan perilaku penyortiran. Salah satu opsi adalah -kn-trueatau [numericOrdering on]:

Jika disetel ke on , setiap urutan Digit Desimal (General_Category = Nd di [ UAX44 ]) diurutkan pada tingkat primer dengan nilai numeriknya. Misalnya, "A-21" <"A-123". Bobot primer yang dihitung semua pada awal kelompok penataan ulang digit . Jadi dengan tabel UCA yang tidak di-backup, "a $" <"a0" <"a2" <"a12" <"a⓪" <"aa".

Namun, dokumen ini adalah "standar teknis" dan bukan bagian dari spesifikasi inti Unicode. Catatan di bagian atas dokumen menyatakan:

Standar Teknis Unicode (UTS) adalah spesifikasi independen. Kesesuaian dengan Standar Unicode tidak menyiratkan kesesuaian dengan UTS.

Oleh karena itu, perilaku khusus ini tidak tersedia di SQL Server atau bahkan dalam. NET (setidaknya tidak secara native), meskipun keduanya sesuai dengan spesifikasi inti Unicode.

Proyek ICU (Komponen Internasional untuk Unicode) adalah seperangkat pustaka C / C ++ dan Java yang mengimplementasikan fungsi ini, dan bahkan ada demo online untuk itu. Dan di bawah "proyek terkait" ada tautan ke proyek .NET yang tampaknya menjadi pembungkus objek COM untuk pustaka ICU yang akan memungkinkan fungsi ini terpapar pada kode yang dikelola. Tetapi tidak jelas apakah proyek .NET itu masih aktif.

Tetapi untuk melihat perilaku ini dalam tindakan, buka Demo ICU Collation .

Rekatkan yang berikut ini ke area teks Input di sebelah kiri:

1
2
10B
6
11
10A
3
10

Tetapkan semua opsi ke "default". Periksa opsi "nomor baris masukan" di sebelah kanan sorttombol, dan pastikan bahwa opsi "kekuatan berbeda" tidak dicentang.

Klik sorttombol dan Anda harus mendapatkan kembali yang berikut:

[1] 1
[8] 10
[6] 10A
[3] 10B
[5] 11
[2] 2
[7] 3
[4] 6

Inilah yang harus diharapkan ketika melakukan pengurutan string yang khas, dan apa yang Anda lihat di SQL Server.

Sekarang, dalam serangkaian tombol radio tepat di atas sorttombol, baris kedua diberi label "numerik". Pilih tombol radio "on".

Klik sorttombol lagi dan Anda harus mendapatkan kembali yang berikut:

[1] 1
[2] 2
[7] 3
[4] 6
[8] 10
[6] 10A
[3] 10B
[5] 11

Mempertanyakan apakah ini berfungsi ketika bagian numerik berada di tengah-tengah string? Oke, rekatkan yang berikut ini ke area teks Input di sebelah kiri (ganti daftar sebelumnya):

Script - 1.sql
Script - 2.sql
Script - 10B.sql
Script - 6.sql
Script - 11.sql
Script - 10A.sql
Script - 3.sql
Script - 10.sql

Pastikan pengaturan numerik masih diatur ke "on". Klik sorttombol lagi dan Anda harus mendapatkan kembali yang berikut:

[1] Script - 1.sql
[2] Script - 2.sql
[7] Script - 3.sql
[4] Script - 6.sql
[8] Script - 10.sql
[6] Script - 10A.sql
[3] Script - 10B.sql
[5] Script - 11.sql

Ingin melihat ini di tempat lain? Buat folder di harddisk Anda, sesuatu seperti C: \ temp \ sorting \ , dan buat file kosong dengan nama "Script -..." yang sama. Lakukan DIRdi jendela perintah dan Anda akan melihat penyortiran standar. Tetapi ketika melihat daftar file di Windows Explorer Anda akan melihat daftar diurutkan menggunakan opsi "numerik" :-).

Solomon Rutzky
sumber
FYI, Postgres 10 memperoleh dukungan untuk pengumpulan ICU. Lihat posting blog ini oleh Peter Eisentraut.
Basil Bourque
@BasilBourque Terima kasih telah menyebutkan tentang PG10. Posting blog itu, pada akhirnya, menyatakan bahwa "ICU menawarkan banyak fungsi di area ini yang belum kami paparkan melalui PostgreSQL. Ada opsi untuk pengurutan case-insensitive, sorting insentitive, dan benar-benar mengkustomisasi sebuah collation. Lihat bagi mereka yang ada di rilis PostgreSQL di masa depan. " Jadi dalam implementasi pertama / saat ini, itu tidak mengubah informasi apa pun dalam jawaban saya. Jika penawaran di masa depan memungkinkan penyortiran numerik, maka saya akan menyebutkannya dalam jawaban saya, tetapi sebagai catatan kaki karena pertanyaan ini khusus untuk SQL Server.
Solomon Rutzky