SQL Server 的 Japanese_CI_AS 其實沒你想像中嚴格:CI、AS、KS、WS 到底是什麼?
Copyright Notice: This article is an original work licensed under the CC 4.0 BY-NC-ND license.
If you wish to repost this article, please include the original source link and this copyright notice.
Source link: https://v2know.com/article/1351
在使用 SQL Server 處理日文資料時,我最近遇到了一個很容易讓人產生誤解的問題:
明明資料庫使用的是:
Japanese_CI_AS
看名字似乎已經是一個相當普通、甚至偏「嚴格」的日文 Collation。
但實際上,SQL Server 在比較某些肉眼看起來不同的字串時,仍然可能把它們判定為相同。
原因就在於:
Japanese_CI_AS
這個名稱裡,還少了兩個對日文非常重要的選項:
KS
WS
一、先拆解 Japanese_CI_AS
SQL Server 的 Windows Collation 名稱並不是隨便取的。
例如:
Japanese_CI_AS
可以拆成:
| 部分 | 全稱 | 意義 |
|---|---|---|
Japanese |
Japanese | 使用日文的排序與比較規則 |
CI |
Case Insensitive | 不區分英文字母大小寫 |
AS |
Accent Sensitive | 區分 Accent |
KS |
Kana Sensitive | 區分平假名與片假名 |
WS |
Width Sensitive | 區分全角與半角 |
問題就在這裡:
Japanese_CI_AS 沒有 KS,也沒有 WS。
而 SQL Server 的規則並不是:
沒寫就代表不管。
恰恰相反。
Microsoft 對 Windows Collation 的定義非常明確:
-
沒有
_KS→ Kana Insensitive -
沒有
_WS→ Width Insensitive
也就是說,Japanese_CI_AS 實際完整含義更接近:
Case Insensitive
Accent Sensitive
Kana Insensitive
Width Insensitive
換句話說:
Japanese_CI_AS
其實是:
不區分大小寫、區分 Accent、不區分平假名/片假名、不區分全角/半角。
這不是 SQL Server 的 Bug,而就是 Collation 本身的設計。
二、KS 是什麼?
KS 是:
Kana Sensitive
也就是:
區分日語平假名與片假名。
例如:
か
カ
它們是不同的 Unicode 字元,但如果使用沒有 _KS 的日文 Collation,SQL Server 的語言學比較規則可以將兩者視為相同。
例如:
SELECT
CASE
WHEN N'か' COLLATE Japanese_CI_AS
= N'カ' COLLATE Japanese_CI_AS
THEN '相同'
ELSE '不同'
END;
而加入:
_KS
之後:
SELECT
CASE
WHEN N'か' COLLATE Japanese_CI_AS_KS
= N'カ' COLLATE Japanese_CI_AS_KS
THEN '相同'
ELSE '不同'
END;
比較規則就會開始區分 Kana 類型。
Microsoft 對 _KS 的官方定義正是:
Distinguishes between the two types of Japanese kana characters: Hiragana and Katakana.
也就是專門區分日語的平假名與片假名。
從這個角度看,KS 幾乎可以說是 SQL Server Collation 中非常「日語特色」的一個選項。
三、WS 又是什麼?
WS 是:
Width Sensitive
也就是:
區分全角與半角。
例如:
カ
カ
以及:
A
A
前者是全角片假名與半角片假名,後者是半角 Latin 字母與全角 Latin 字母。
如果 Collation 沒有 _WS,SQL Server 在語言學比較時可以把這類 Width 差異忽略。
加入:
_WS
以後,才會把它們當成不同。
例如:
SELECT
CASE
WHEN N'カ' COLLATE Japanese_CI_AS
= N'カ' COLLATE Japanese_CI_AS
THEN '相同'
ELSE '不同'
END;
與:
SELECT
CASE
WHEN N'カ' COLLATE Japanese_CI_AS_WS
= N'カ' COLLATE Japanese_CI_AS_WS
THEN '相同'
ELSE '不同'
END;
比較結果就會不同。
Microsoft 對 _WS 的定義就是區分 full-width 與 half-width characters。
與 _KS 不同,_WS 並不是日語專屬概念。
因為中文、韓文以及東亞環境中同樣可能存在:
A
A
這類全角/半角差異。
四、所以 Japanese_CI_AS_KS_WS 是什麼?
現在再看:
Japanese_CI_AS_KS_WS
就非常容易理解了:
Japanese
│
├─ CI → Case Insensitive
│ 大小寫不區分
│
├─ AS → Accent Sensitive
│ Accent 區分
│
├─ KS → Kana Sensitive
│ 平假名 / 片假名區分
│
└─ WS → Width Sensitive
全角 / 半角區分
因此:
| 比較 | Japanese_CI_AS |
Japanese_CI_AS_KS_WS |
|---|---|---|
か / カ |
不區分 | 區分 |
カ / カ |
不區分 | 區分 |
A / A |
不區分 | 區分 |
A / a |
不區分 | 不區分 |
最後一行尤其重要。
五、Japanese_CI_AS_KS_WS 也不是「所有文字都精確比較」
看到:
Japanese_CI_AS_KS_WS
後,也不要走到另一個極端,把它理解成:
所有不同字元全部都一定不相等。
因為它仍然有:
CI
也就是:
Case Insensitive
所以:
A
a
仍然會被視為相同。
如果你的需求真的是:
大小寫、Accent、假名、全半角全部都要區分
那麼對應的應該是:
Japanese_CS_AS_KS_WS
其中:
CS = Case Sensitive
Microsoft 的 Collation 比較屬性表中,也把 _CS_AS_KS_WS 定義為大小寫、Accent、Kana、Width 全部敏感。
因此可以簡單整理成:
| Collation | 大小寫 | Accent | 平/片假名 | 全/半角 |
|---|---|---|---|---|
Japanese_CI_AI |
× | × | × | × |
Japanese_CI_AS |
× | ✓ | × | × |
Japanese_CI_AS_KS |
× | ✓ | ✓ | × |
Japanese_CI_AS_WS |
× | ✓ | × | ✓ |
Japanese_CI_AS_KS_WS |
× | ✓ | ✓ | ✓ |
Japanese_CS_AS_KS_WS |
✓ | ✓ | ✓ | ✓ |
其中:
× = 不區分
✓ = 區分
六、一個非常反直覺的設計:KI 和 WI 為什麼看不到?
看到這裡可能還會冒出一個問題。
既然:
KS = Kana Sensitive
WS = Width Sensitive
那麼為什麼沒有:
KI = Kana Insensitive
WI = Width Insensitive
例如:
Japanese_CI_AS_KI_WI
?
因為 SQL Server 的命名規則就是:
Kana 與 Width 在「Insensitive」時直接省略。
Microsoft 給出的 Windows Collation 語法就是:
<CaseSensitivity>_<AccentSensitivity>
[_<KanatypeSensitive>]
[_<WidthSensitive>]
也就是 KS 和 WS 都是可選項。
不寫 _KS 就代表 Kana Insensitive;
不寫 _WS 就代表 Width Insensitive。
所以:
Japanese_CI_AS
實際上可以在人腦裡展開成:
Japanese_CI_AS_KI_WI
只是 SQL Server 沒有這種正式寫法。
這也是 Japanese_CI_AS 如此容易造成誤解的原因。
名字看起來很短、很正常,但實際上背後偷偷省略了兩個「Insensitive」。
七、KS 是不是只有 Japanese Collation 才有?
有趣的是:語義上非常日語,但語法上不是 Japanese 專屬。
SQL Server Windows Collation 的統一命名規則本身允許:
_KS
_WS
出現在其他語系 Collation 中。
Microsoft 官方文件甚至使用:
Traditional_Spanish_CS_AS_KS_WS
作為 COLLATIONPROPERTY 的示例。
這看起來非常奇怪:
Traditional Spanish
+
Kana Sensitive
西班牙語哪來的平假名和片假名?
原因其實很簡單:
KS 是 SQL Server Windows Collation 比較模型的一個通用屬性位,而不是「Japanese Collation 專用語法」。
只是對西班牙語正常業務資料而言,Kana sensitivity 基本沒有實際意義。
所以更準確的說法是:
_KS在 SQL Server 的語法機制上不是 Japanese 專屬,但它所控制的比較概念本身就是日本語 Kana。
八、這對實際業務系統有什麼影響?
這才是最值得注意的地方。
假設有一個欄位:
氏名
署名
住所
地名
商品名
然後程式裡寫:
WHERE 署名 = @署名
很多開發者看到 =,自然會理解成:
資料庫中的文字必須和參數一樣。
但 SQL Server 的 = 並不是單純逐 Unicode Code Point 比較。
它會受到欄位 Collation 的影響。
所以如果欄位是:
Japanese_CI_AS
你實際要求 SQL Server 做的是:
按 Japanese_CI_AS 的語言學規則判斷這兩個字串是否「等價」。
這和:
兩邊每個字元是不是完全一樣
其實完全是兩件事。
因此 Collation 不只是影響:
ORDER BY
它還會直接影響:
=
<>
LIKE
IN
JOIN
GROUP BY
DISTINCT
以及依賴文字比較結果的唯一性等業務邏輯。
這也是為什麼 Collation 選錯之後,問題可能不是單純「排序看起來有點怪」,而是會直接變成業務 Bug。
九、Japanese_CI_AS 到底是不是一個壞 Collation?
也不能這麼說。
Japanese_CI_AS 的設計本身是有合理性的。
例如做:
使用者搜尋
商品搜尋
名稱搜尋
關鍵字搜尋
時,你可能根本不希望使用者因為輸入:
全角 / 半角
平假名 / 片假名
不同,就完全搜不到資料。
這時候 Kana Insensitive、Width Insensitive 反而非常方便。
問題在於:
「搜尋方便」和「資料是否完全相同」是兩種不同需求。
如果今天做的是:
模糊搜尋
Japanese_CI_AS 的寬鬆規則可能是優點。
但如果做的是:
識別
唯一判定
業務鍵
精確比對
那這種「幫你把不同字元視為等價」的行為就可能成為陷阱。
十、最後用一句話理解
以後看到:
Japanese_CI_AS
不要只讀成:
Japanese、Case Insensitive、Accent Sensitive。
最好在腦子裡把它補完整:
Japanese
CI
AS
Kana Insensitive
Width Insensitive
而:
Japanese_CI_AS_KS_WS
則是:
大小寫仍然不區分,但 Accent、平假名/片假名、全角/半角全部區分。
如果連大小寫都必須區分:
Japanese_CS_AS_KS_WS
才是四種 Linguistic sensitivity 全開。
至於如果需求進一步變成:
我根本不想讓 SQL Server 幫我判斷「語言學上是不是差不多」,我就是要按照字元/碼位做非常嚴格的比較。
那就已經是另一個話題了:
BIN
BIN2
尤其是 BIN2。
而這也正好說明了一件事:
SQL Server 的 Collation 從來不只是「排序規則」。
它實際決定的是:
SQL Server 到底認為哪兩段文字算「一樣」。
在日文系統中,KS 與 WS 往往就是最容易被忽略、也最容易造成誤判的兩個選項。
This article was last edited at