Bagaimana satu "karakter" (yang dapat terdiri dari beberapa Poin Kode: pasangan pengganti, menggabungkan karakter, dll) dibandingkan dengan yang lain didasarkan pada seperangkat aturan yang agak rumit. Sangat rumit karena harus memperhitungkan semua berbagai (dan kadang-kadang "aneh") aturan yang ditemukan di semua bahasa yang diwakili dalam spesifikasi Unicode . Sistem ini berlaku untuk Collations non-biner untuk semua NVARCHAR
data, dan untuk VARCHAR
data yang menggunakan Windows Collation dan bukan SQL Server Collation (satu yang dimulai dengan SQL_
). Sistem ini tidak berlaku untuk VARCHAR
data yang menggunakan SQL Server Collation karena mereka menggunakan pemetaan sederhana.
Sebagian besar aturan didefinisikan dalam Algoritma Kolom Unicode (UCA) . Beberapa aturan itu, dan beberapa tidak tercakup dalam UCA, adalah:
- Pemesanan default / berat yang diberikan dalam
allkeys.txt
file (tercantum di bawah)
- Sensitivitas dan opsi mana yang digunakan (mis. Apakah sensitif huruf atau tidak sensitif ?, dan jika sensitif, maka apakah huruf besar lebih dulu atau lebih rendah?)
- Setiap penimpaan berbasis lokal.
- Versi standar Unicode sedang digunakan.
- Faktor "manusia" (yaitu Unicode adalah spesifikasi, bukan perangkat lunak, dan dengan demikian diserahkan kepada masing-masing vendor untuk mengimplementasikannya)
Saya menekankan bahwa poin terakhir mengenai faktor manusia semoga menjelaskan bahwa kita tidak boleh berharap SQL Server untuk selalu berperilaku 100% sesuai dengan spesifikasi.
Faktor utama di sini adalah bobot yang diberikan untuk masing-masing Poin Kode, dan fakta bahwa beberapa Poin Kode dapat berbagi spesifikasi bobot yang sama. Anda dapat menemukan bobot dasar (tanpa pengesampingan khusus lokal) di sini (saya percaya 100
seri Collations adalah Unicode v 5.0 - konfirmasi informal dalam komentar pada item Microsoft Connect ):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
Poin Kode dalam pertanyaan - U + FFFD - didefinisikan sebagai:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
Notasi itu didefinisikan di bagian 9.1 Format File Allkeys dari UCA:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Baris terakhir itu penting karena Poin Kode yang kita lihat memiliki spesifikasi yang memang dimulai dengan "*". Di bagian 3.6 Pembobotan Variabel ada empat perilaku yang mungkin ditentukan, berdasarkan nilai konfigurasi Collation yang kami tidak memiliki akses langsung ke (ini adalah hard-coded ke dalam implementasi Microsoft setiap Collation, seperti apakah case-sensitive menggunakan huruf kecil terlebih dahulu atau huruf besar terlebih dahulu, properti yang berbeda antara VARCHAR
data menggunakan SQL_
Collations dan semua variasi lainnya).
Saya tidak punya waktu untuk melakukan penelitian penuh ke jalur mana yang diambil dan untuk menyimpulkan opsi mana yang digunakan sehingga bukti yang lebih solid dapat diberikan, tetapi aman untuk mengatakan bahwa dalam setiap spesifikasi Poin Kode, apakah sesuatu dianggap "sama" tidak akan selalu menggunakan spesifikasi lengkap. Dalam hal ini, kami memiliki "0F12.0020.0002.FFFD" dan kemungkinan besar hanya level 2 dan 3 yang digunakan (yaitu .0020.0002. ). Melakukan "Hitung" di Notepad ++ untuk ".0020.0002." menemukan 12.581 pertandingan (termasuk karakter tambahan yang belum kami tangani). Melakukan "Hitung" pada "[*" mengembalikan 4049 pertandingan. Melakukan RegEx "Cari" / "Hitung" menggunakan pola\[\*\d{4}\.0020\.0002
mengembalikan 832 hasil. Jadi di suatu tempat dalam kombinasi ini, plus kemungkinan beberapa aturan lain yang tidak saya lihat, ditambah beberapa detail implementasi khusus Microsoft, adalah penjelasan lengkap tentang perilaku ini. Dan untuk menjadi jelas, perilakunya sama untuk semua karakter yang cocok karena mereka semua cocok satu sama lain karena mereka semua memiliki bobot yang sama begitu aturan diterapkan (artinya, pertanyaan ini bisa ditanyakan tentang salah satu dari mereka, bukan tentu Pak �
).
Anda dapat melihat dengan kueri di bawah ini dan mengubah COLLATE
klausa sesuai hasil di bawah kueri bagaimana berbagai sensitivitas bekerja di dua versi Collations:
;WITH cte AS
(
SELECT TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARBINARY(2), cte.Num) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0xFFFD) COLLATE Latin1_General_100_CS_AS_WS --N'�'
ORDER BY cte.Num;
Berbagai jumlah karakter yang cocok pada berbagai koleksi ada di bawah ini.
Latin1_General_100_CS_AS_WS = 5840
Latin1_General_100_CS_AS = 5841 (The "extra" character is U+3000)
Latin1_General_100_CI_AS = 5841
Latin1_General_100_CI_AI = 6311
Latin1_General_CS_AS_WS = 21,229
Latin1_General_CS_AS = 21,230
Latin1_General_CI_AS = 21,230
Latin1_General_CI_AI = 21,537
Dalam semua koleksi yang tercantum di atas N'' = N'�'
juga mengevaluasi true.
MEMPERBARUI
Saya dapat melakukan sedikit riset lebih lanjut dan inilah yang saya temukan:
Bagaimana "mungkin" seharusnya bekerja
Dengan menggunakan ICU Collation Demo , saya mengatur lokal ke "en-US-u-va-posix", mengatur kekuatan menjadi "primer", memeriksa acara "mengurutkan kunci", dan menempelkan 4 karakter berikut yang saya salin dari hasil kueri di atas (menggunakan Latin1_General_100_CI_AI
Collation):
�
Ԩ
ԩ
Ԫ
dan itu mengembalikan:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
Kemudian, periksa properti karakter untuk " " di http://unicode.org/cldr/utility/character.jsp?a=fffd dan lihat bahwa kunci sortir level 1 (yaitu FF FD
) cocok dengan properti "uca". Mengklik pada properti "uca" itu akan membawa Anda ke halaman pencarian - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D - hanya menampilkan 1 pertandingan. Dan, dalam file allkeys.txt , berat jenis level 1 ditampilkan sebagai 0F12
, dan hanya ada 1 kecocokan untuk itu.
Untuk memastikan bahwa kami menafsirkan perilaku dengan benar, saya melihat karakter lain: SURAT MODAL YUNANI OMICRON DENGAN VARIA Ὸ
di http://unicode.org/cldr/utility/character.jsp?a=1FF8 yang memiliki "uca" ( yaitu level 1 sortir berat / elemen penyusun) dari 5F30
. Mengklik "5F30" membawa kita ke halaman pencarian - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - menampilkan 30 pertandingan, 20 dari mereka berada dalam kisaran 0 - 65535 (yaitu U + 0000 - U + FFFF). Mencari di file allkeys.txt untuk Code Point 1FF8 , kita melihat berat jenis level 1 12E0
. Melakukan "Hitung" di Notepad ++ on12E0.
memperlihatkan 30 pertandingan (ini cocok dengan hasil dari Unicode.org, meskipun tidak dijamin karena file tersebut untuk Unicode v 5.0 dan situs ini menggunakan data Unicode v 9.0).
Di SQL Server, kueri berikut mengembalikan 20 kecocokan, sama seperti pencarian Unicode.org saat menghapus 10 karakter tambahan:
;WITH cte AS
(
SELECT TOP (65535) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARCHAR(50), CONVERT(VARBINARY(2), cte.Num), 2) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0x1FF8) COLLATE Latin1_General_100_CI_AI
ORDER BY cte.Num;
Dan, untuk memastikan, kembali ke halaman Demo ICU Collation, dan mengganti karakter di kotak "Input" dengan 3 karakter berikut diambil dari daftar 20 hasil dari SQL Server:
Ὂ
𝜪
Ὸ
menunjukkan bahwa mereka, memang, semua memiliki 5F 30
bobot jenis level 1 yang sama (cocok dengan bidang "uca" pada halaman properti karakter).
JADI, sepertinya memang karakter khusus ini tidak cocok dengan yang lain.
Bagaimana cara kerjanya sebenarnya (setidaknya di Microsoft-land)
Tidak seperti dalam SQL Server, .NET memiliki sarana untuk menunjukkan kunci pengurutan untuk string melalui Metode CompareInfo.GetSortKey . Menggunakan metode ini dan meneruskan hanya karakter U + FFFD, ia mengembalikan semacam kunci 0x0101010100
. Kemudian, ulangi semua karakter dalam kisaran 0 - 65535 untuk melihat mana yang memiliki kunci semacam 0x0101010100
4545 pertandingan yang dikembalikan. Ini tidak persis cocok dengan 5840 yang dikembalikan dalam SQL Server (saat menggunakan Latin1_General_100_CS_AS_WS
Collation), tetapi ini adalah yang terdekat yang bisa kita dapatkan (untuk saat ini) mengingat bahwa saya menjalankan Windows 10 dan .NET Framework versi 4.6.1, yang menggunakan Unicode v 6.3.0 sesuai dengan bagan untuk Kelas CharUnicodeInfo(di "Catatan untuk Penelepon", di bagian "Keterangan"). Untuk saat ini saya menggunakan fungsi SQLCLR sehingga tidak dapat mengubah versi Kerangka target. Ketika saya mendapat kesempatan, saya akan membuat aplikasi konsol dan menggunakan versi Kerangka target 4,5 karena yang menggunakan Unicode v 5.0, yang harus cocok dengan 100 series Collations.
Apa yang ditunjukkan oleh tes ini adalah bahwa, bahkan tanpa jumlah yang sama persis antara .NET dan SQL Server untuk U + FFFD, cukup jelas bahwa ini bukan perilaku khusus SQL Server, dan apakah disengaja atau keliru dengan implementasi yang dilakukan oleh Microsoft, karakter U + FFFD memang cocok dengan beberapa karakter, bahkan jika tidak sesuai dengan spesifikasi Unicode. Dan, mengingat bahwa karakter ini cocok dengan U + 0000 (nol), itu mungkin hanya masalah bobot yang hilang.
JUGA
Mengenai perbedaan perilaku dalam =
kueri vs LIKE N'%�%'
kueri, itu ada hubungannya dengan wildcard dan bobot yang hilang (saya asumsikan) untuk � Ƕ Ƿ Ǹ
karakter (yaitu ) ini. Jika LIKE
kondisi diubah menjadi sederhana LIKE N'�'
maka mengembalikan 3 baris yang sama dengan =
kondisi. Jika masalah dengan wildcard bukan karena bobot "hilang" (tidak ada 0x00
kunci pengurutan yang dikembalikan oleh CompareInfo.GetSortKey
, btw) maka mungkin karena karakter ini berpotensi memiliki properti yang memungkinkan kunci sortir bervariasi berdasarkan konteks (yaitu karakter di sekitarnya ).
FFFD
(mencari*0F12.0020.0002.FFFD
hanya mengembalikan satu hasil). Dari pengamatan @ Forrest bahwa mereka semua cocok dengan string kosong dan sedikit lebih banyak membaca tentang subjek sepertinya berat yang mereka bagikan dalam berbagai non binary collations sebenarnya nol saya percaya.� A a \u24D0
serta beberapa lainnya yang ada di set hasil pertandingan 5839. Sepertinya Anda tidak dapat melewati bobot pertama, dan char pengganti ini adalah satu-satunya yang dimulai dengan0F12
. Banyak yang lain juga memiliki bobot pertama yang unik, dan banyak yang hilang dari file allkey sepenuhnya. Jadi ini bisa menjadi bug implementasi karena kesalahan manusia. Saya memang melihat arang ini di grup "tidak didukung" di situs Unicode di bagan Collations mereka. Akan terlihat lebih banyak besok.01 01 01 01 00
itu disebutkan di sini archives.miloush.net/michkap/archive/2007/09/10/4847780.html (sepertiCompareInfo.InternalGetSortKey
panggilanLCMapStringEx
)