背景:字符的存儲原理
我們知道,在計算機內部,無論是字符還是數字,最終都是通過轉換成二進制數(也就是內碼)存儲在計算機中。
字符集是一組字符的集合,它裡面包含了單個字符與其內碼(二進制值的一一對應關系。比如說,a的內碼是65=01100101
設想以下情形:
S
是數據庫,C,B是客戶端。
C
通過ODBC與S連接,C的字符集環境是BIG5。
B
通過JDBC與S連接,B的字符集環境是unicode,utf16。
C
向S發現一條SQL:
UPDATE TABLE1 SET FIELD1 = '
更新值
' WHERE FIELD1 = '
條件值
',
S
收到請求後,將
'
更新值
'
存儲到數據庫中
。
那麼,有以下幾個疑問:
1.
C
向S發送SQL:
UPDATE TABLE1 SET FIELD1 = '
更新值
' WHERE FIELD1 = '
條件值
'
前,肯定是先將這條字面上的SQL語句轉成二進制了。因為不同的字符集,同一個字面串轉成的內碼是不一樣的,那請問,C是參照哪個字符集將這條語句轉成內碼的?
2.
第一個問題中的字面串到內碼的轉換工作是由編程語方言的編譯囂做的,還是由JDBC或ODBC來做的?
3.
JDBC
或ODBC還會不會在字符內碼上做一些轉換工作?
4.
完成轉換後,再把這一些二進制數據通過網絡發送到數據庫服務囂之後。如果數據的字符集與客戶端C的字符集是一致的,這似乎沒有什麼問題。
但是如果S與C兩端的字符集是不一樣的,在這種情況下,數據庫在存儲'更新值'之前,會不會檢查符集的一致性,並做相應的內碼轉換? 也就是將'更新值'由B的字符集的內碼轉換為S的字符集的內碼?
我覺得數據庫是不會做這個事情的,數據庫只是原原本本的將這段從客戶端發送來的'更新值'的內碼二進制值直接進行存儲。那如果是這樣的話,就有一個問題了:因為S的字符集與C是不一樣的,當C再向S發送:
select * from table1 where field1 = '
條件值
'
的時候,讀取正確。這時若有另一個客戶端B,它的字符集與C的字符集是不一樣的,它也向S發送了這條SQL:select * from table1 where field1 = '條件值'。因為字符集不一樣,雖然字面上看起來C和B發送的是同一條SQL語句,但是在做完字符到內碼的轉換後,兩條語句是不一樣的,再將語句傳到S,顯然這就變成兩條不同的SQL,返回的查詢結果也會不同。所以造成C1讀取的結果不正確,或返回的結果亂碼了。
沒有做過測試,不確定這個理解是否正確。
如說在S,B,C三者的字符集不一致的情況下,還會不會造成一些其它的問題?
----------------------------
2007.7.2
今天實驗發現,oracle的字符集設置與sqlserver的設置行為是不一樣的。
在oracle中,只有數據庫層面的字符集。而與字符集相關的排序規格、字符比較規則、語言等是可以在數據庫運行中動態修改的。
而在 sql server中,分為Server層的字符集(在安裝sql server時指定)、database層的字符集(在創建一個新數據庫時指定)、字段的字符集(在創建一個數據表時指定)。在sql server 中,字符集是COLLATE的一部分。COLLATE包含字符集,排序、字符比較規則等。可以在實例中創建與實例的COLLATE不一樣的COLLATE的數據庫,也可以在數據庫中創建與數據庫的COLLATE不一樣的表的字段。
也就是說,
在oracle中橫向的控制粒度比較細(字符集、語言、排序規格可以獨立控制,但字符集是數據庫全局的,在創建數據庫時確定,不能動態修改);
而在Sql server中,縱向的控制粒度比較細(可以從server,database,column三個層面來設置字符集及相關的排序規格,但字符集與排序規則這些是作為一個整體設置的,且一旦指定後不可修改。)