你可能不知道的字元比較中的「秘密」

2021-09-22 20:31:27 字數 3117 閱讀 9796

原文:

你可能不知道的字元比較中的「秘密」

有時候,乙個簡單的字元比較,你可能也會被弄得暈頭轉向。為什麼這樣說呢?請看下面這個例子(**就不貼了,因為後來發現頁面不支援這兩個字元的顯示)。猜測一下,會是什麼結果?是1還是0? 回答這個問題之前,請再繼續向下看。先建立幾個不同排序規則的資料庫(見資料庫名可知)。

figure-1: 在sql_latin1_general_cp1_ci_as排序規則下的比較

figure-2: 在chinese_prc_ci_as排序規則下的比較

在sql_latin1_general_cp1_ci_as或是chinese_prc_ci_as兩不同的排序規則下,這兩個字元竟然是相等的!!很明顯看起來就是不相等嘛!使用了unicode函式轉換也無效。怎麼回事呢?!下面就是這個問題的解答。原文是英文,我已經翻譯成中文(英語水平有限,錯漏難免,最好還是看原文哈)。

未定義的字元在unicode或windows排序規則中的排序問題

當在非二進位制排序規則中比較兩個unicode字串時,sql server使用乙個跟windows api comparestringw基本相同的庫。這個庫為每個已經辨識的字元定義了乙個權重值(weight),並以這個權重值進行字元的比較。然而,並不是所有**點(code point)都已經在排序庫中進行了定義。它們可能被未定義的原因是:

**點在unicode標準中未進行定義。

**點在unicode標準中已進行了定義,但在windows中卻未進行定義。這需要花費時間和精力為新的字元定義語言語義的排序。windows團隊通常需要與當時標準組織和/或地區專案經理合作,為新的字元定義排序規則。他們在每個版本中新增新字元的支援,並試圖迎頭趕上。有些字元也許已經具有字型的定義,因此可能會正常地顯示,但仍然沒有對比較進行定義。比如nchar(13144) - nchar(13174)。

**點在windows進行了定義,但未在sql server中定義。

windows nls團隊已經決定,未定義的字元進行比較時將被忽略,部分原因是沒有乙個好的辦法將未定義的字元和其他已經定義的字元進行比較。sql server繼承了這一語義。這確實導致了一些令人感覺困惑的行為。看看下面的例子。

declare

@undefined_char1

nvarchar(10), @undefined_char2

nvarchar(10

)set

@undefined_char1

=nchar(0x0000

)set

@undefined_char2

=nchar(13144

)select

'undefine characters compare equal to empty string

'where

@undefined_char1=''

select

'all undefine characters compare equal

'where

@undefined_char1

=@undefined_char2

go

create

table t (c nvarchar(10

))go

create

unique

index it on

t(c)

go--

first insert succeeds, but second insert fails with duplicate key error.

insert t values (nchar(0x0000

))insert t values (nchar(13144

))go

正如你所見,由於所有未定義的字元比較都為相等的,他們會導致重複鍵的錯誤。同理,如果你建立一張未定義字元為表名的表,然後嘗試建立另乙個未定義字元為表名的表,第二張表會因為表名重複而建立失敗,即使這兩個未定義字元的**點是不同的。這也可能導致混淆的結果出現在如charindex, patindex或like等內建的字串匹配(功能)中。 

雖然這些結果似乎令人迷惑不解,但基本規則其實很簡單。即未定義字元和字串的比較將被忽略。一旦你明白並記住這個規則,這個行為就很容易理解了。

只要有未定義字元的引數將被忽略。由於這是在windows平台上的行為,沒有乙個絕對的更好的方法對它們進行排序,並且向右相容,我們要保持這種行為。

如果你的應用程式要使用到這些未定義的字元,並且把它們當成常規字元處理,你可以使用二進位制的排序規則(binary collation)。在二進位制排序規則中,比較完全是根據**點,不是語言規則,因此也沒有所謂的已定義和未定義的概念了。(完)

讀完這篇部落格,你應該明白怎麼回事了吧。原因就是:跟未進行定義的字元作比較時,sql server視為相等的。如果實際應用中要視為常規字元,轉為二進位制比較即可。因為二進位制的比較就單純的按照字元的**點比較了。所以解決最開始的問題不難,如下:

figure-3: 在latin1_general_bin排序規則下的比較

不難推導出,在二進位制排序規則下的資料庫中,預設下,這兩個字元是不相等的。

figure-4: 在latin1_general_bin排序規則下的比較

figure-5: 在sql_latin1_general_cp1_ci_as排序規則下無法插入兩行記錄

figure-6: 在latin1_general_bin排序規則下,成功插入這兩行記錄。

你可能不知道的東西

元素可以分為塊級元素,行內元素以及行內塊級元素。行內元素的margin或者padding只有margin left和margin right以及padding left和padding right有效果,margin top margin bottom padding top padding bot...

你可能不知道的const

眾所周知,使用 const 宣告的變數必須同時初始化為某個值。一經宣告,在其生命週期的任何時候都不能再重新賦予新值 const a syntaxerror 常量宣告時沒有初始化 const b 3 console.log b 3 b 4 typeerror 給常量賦值const 宣告只應用到頂級原語...

你可能不知道的python logging機制

先來看幾個例子,先想一下執行結果,答案稍後揭曉 例一 mylogger logging.getlogger mylogger mylogger.info mylogger info mylogger.warning mylogger warning 例二 mylogger logging.getlo...