SQL Server 的 Japanese_CI_AS 其實沒你想像中嚴格:CI、AS、KS、WS 到底是什麼?

| SQL | 1 Reads

在使用 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 不區分 不區分

最後一行尤其重要。


五、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>]

也就是 KSWS 都是可選項。

不寫 _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

西班牙語哪來的平假名和片假名?

原因其實很簡單:

KSSQL 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 到底認為哪兩段文字算「一樣」。

在日文系統中,KSWS 往往就是最容易被忽略、也最容易造成誤判的兩個選項。

This article was last edited at