Question:
What is Collation? How it will affect SQL Server database, and server?
Answer:
字符的存儲
在將collation之前,我們首先需要知道字符是如何被存儲的。在計算機中,所有數據都是用0和1這樣的位來描述。一個字節有8位,因此一個字節最多可以描述256個字符。在歐美國家,比如美國,他們的文字字符主要就是26個字母加上一些特殊符號(+-*/等),用一個字節就可以存儲,一個國家使用的所有字符就是一個code page,用一個字節存儲字符的code page 叫做single-byte code page。但是在亞洲的一些國家,比如中國,常用漢字有幾萬個,根本不能用一個字節來表示所有的漢字字符,因此需要用兩個字節描述。因為兩個字節有16位,最多可以描述65536個字符,足夠用來描述所有漢語字符以及常用字符,這些字符也是一個code page,不過是double-byte code page,主要針對的是中國。每個國家都有一個code page來對應所使用的字符。比如歐美國家,他們使用拉丁,雖然a-z這26個字母所對應的二進制在code page中是相同的,但是在重音('é'和'á')方面是不相同的。所以如果code page不同,那么相同的二進制代碼所表示出來的字符也可能不相同。如果數據在不同code page的計算機上傳輸,就需要進行code page的轉換,如果接收方的code page上沒有定義傳輸方傳送過來的特定字符的二進制位,那么就會出現數據丟失。
Collation的組成
在講完字符的存儲形式以后,我們就可以講什么是collation了。Collation描述了數據在數據庫中是按照什么規則來描述字符,以及字符時如何被排序和比較的。在SQL Server中,Collation由兩部分組成,比如中國的一個collation是 Chinese_PRC_CI_AI_WS ,前半部份是指的是所支持的字符集,與code page相對應,如Chinese_PRC 對應的代碼頁是936,在這個code page中定義了所有能夠使用的字符。后半部CI_AI_WS用於表示排序規則,比如:
- _CI(CS)表示是否區分字母大小寫,CI不區分,CS區分。如果區分大小寫,那么排序的時候小寫字母的排在大寫的前面;如果不區分大小寫,那么排序的時候視大小寫字母相同。
- _AI(AS) 表示是否區分重音,AI不區分,AS區分。如果不區分重音,那么排序的時候視“a”和“ấ”為相同字符
- _KI(KS) 表示是否區分假名類型,KI不區分,KS區分。在日語中應用。
- _WI(WS) 表示是否區分全半角,WI不區分,WS區分。半角是單字節,全角是雙字節。
Collation的四個級別
Collation一共有四個級別,分別是server-level, database-level, column-level和expression-level。
Server-level Collations:
服務器級別的collation是在安裝數據庫實例的時候指定的,如果沒有特別指定,那么就將windows collation作為server-level collation。Windows collation由操作系統中的區域語言來決定的,如下圖所示。
因為我們選擇的是Chinese(Simplified,PRC),那么我們默認的server-level collations就是:Chinese_PRC_。Server-level collation也是系統數據庫和用戶數據庫的默認collation。一般情況下server-level collation一旦設定就不能更改,除非將所有數據庫中的對象以及數據全部導出,並創建master,再將數據導回才可完成。
Database-level collations
Database-level collations可以在create database…collate的時候指定,如果要修改database-level collations,可以通過alter database …collate來修改。一般情況是不能修改系統數據庫(master等)的collations的,除非使用前面提到的修改server-level collations的方法來修改系統數據庫。
Column-level collations
在創建或更改表時,可使用 COLLATE 子句指定每個字符串列的排序規則。當然也可以修改column-level collations。
Expression-level collations
Expression-level是指在執行sql語句的時候指定collations,比如:
SELECT name FROM customer ORDER BY name COLLATE Latin1_General_CS_AI;
這一條查詢語句表示按照Latin1_General_CS_AI的排序規則來進行排序。Expression-level collations的一個好處就是非常靈活。
實驗1:解決collation不匹配導致的沖突
在對兩個collations級別不同的數據庫的表進行連接操作的時候,會報錯。這是可以通過expression-level collations來指定使用何種collations來解決問題。比如使用Collate Database_Default 則會將字段定義或轉換成當前數據庫的默認排序規則,從而解決沖突。
Step1:
創建兩張表,第一張表使用默認的collation,第二張表在stuname列上指定collation。
create table student1 ( stuid int not null, stuname nvarchar(20) not null, ); create table student2 ( stuid int not null, stuname nvarchar(20) COLLATE Latin1_General_CS_AI not null, );
--求表連接Step2:
select s1.*,s2.* from student1 s1,student2 s2 where s1.stuname=s2.stuname
執行上述查詢報錯如下所示:
Cannot resolve the collation conflict between "Latin1_General_CS_AI" and "Chinese_PRC_CI_AS" in the equal to operation.
然后在expression-level使用Collate Database_Default
select s1.*,s2.* from student1 s1,student2 s2 where s1.stuname=s2.stuname Collate Database_Default
上述查詢執行成功。
需要注意的是collation只能用在字符串類型的列上面,如果在int列上使用collate會報錯。
實驗2:變更collation對數據庫的影響
目的1:
創建數據庫,查看數據的默認database collation與server collation是否一樣。
目的2:
在database collation為Chinese_PRC_CI_AS的數據庫中插入中文,然后修改collation為Latin1_General_CS_AI,看看已保存的數據有沒有發生變化。如果再次把collation改回到Chinese_PRC_CI_AS,又有什么變化
目的3:
在collation為Latin1_General_CS_AI的情況下,插入中文,會有什么情況,如何解決。
--實驗1:測試nvarchar和varchar的存儲長度 --創建一個默認collation為Chinese_PRC_CI_AS的數據庫TESTDB3 USE TESTDB1 CREATE TABLE test ( lastname NVARCHAR(8) NOT NULL,--nvarchar類型,雙字節存儲 title VARCHAR(8) NOT NULL, --varchar類型,單字節存儲 ); insert into test values('姓名1','標題1'); select * from test; insert into test values('123456789','1');--String or binary data would be truncated. insert into test values('12345678','1'); insert into test values('1','12345678'); insert into test values('一二三四五六七八','一二三四'); select * from test; --總結: /* 1.nvarchar(n),按字符來存儲,不論是英文字符還是中文字符。最多能夠存儲n個中文或者是英文,但是所占用的存儲空間是2n+2個字節。1<=n<=4000 2.varchar(n)按字節存儲,最多能夠存儲n個英文字母,存儲n/2個中文字符。但是所占用的存儲空間是n個字節。1<=n<=8000 */ --實驗2:collation的變更對數據的影響。 USE TESTDB1 select * from test; --step1:修改數據庫的collation從默認的Chinese_PRC_CI_AS修改為Latin1_General_CS_AI,英語國家都是使用這個排序規則。 use master alter database TESTDB1 collate Latin1_General_CS_AI --step2:通過下面的語句可以查出實例中與默認collation不同的數據庫,查找到了我們之前的 TESTDB1 use master SELECT NAME AS DATABASE_NAME , DATABASEPROPERTYEX(NAME,'COLLATION') AS DBCOLLATION , SERVERPROPERTY('COLLATION') AS SERVERCOLLATION FROM SYS.DATABASES WHERE CONVERT(SYSNAME,DATABASEPROPERTYEX(NAME,'COLLATION')) <> SERVERPROPERTY('COLLATION') --step3:在修改完collation以后查看表中的數據,發現數據沒有改變。 USE TESTDB1 select * from test; --step4:在新的collation下面插入數據 insert into test values('姓名1','標題1');--插入以后發現中文都變成了亂碼"??" select * from test; --step5:因為lastname是nvarchar類型,我們在插入的時候指定出nvarchar insert into test values(N'姓名2','標題2');--此時發現姓名沒有亂碼,標題不用說還是亂碼?? select * from test; --step6:試一試在varchar類型的字段插入nvarchar是數據 insert into test values(N'姓名3',N'標題3');--發現標題也不是亂碼了。 select * from test; ------------------------------------ --step7:將collation從Latin1_General_CS_AI改回默認的Chinese_PRC_CI_AS use master alter database TESTDB1 collate Chinese_PRC_CI_AS --step8:在修改完collation以后查看表中的數據,發現數據沒有改變。 USE TESTDB1 select * from test; --step9:插入字符,不指定unicode類型,查詢顯示的是中文,表明當前collation默認使用unicode編碼。 insert into test values('姓名4','標題4'); select * from test;
總結:
- collation的變更不改變數據庫原先存儲的數據,原來是怎么樣,修改以后還是怎樣,沒有發生改變。
- Latin1_General_CS_AI默認是的non-unicode的,所以在這個collation下插入中文變成亂碼,必須在插入數據的時候指明使用unicode形式插入,也就是添加關鍵字“N”,而Chinese_PRC_CI_AS這個collation使用的是double-byte code page,這里面定義了所有中文字符,所以在插入數據的時候不需要指定關鍵字“N”。
- 可以往varchar數據類型的列中插入nvarchar的數據,也就是使用varcha存儲unicode的數據。