通過oracle類比MySQL中的位元組字元問題
在幾個月前寫過一篇博文 MySQL資料型別 http://blog.itpub.net/23718752/viewspace-1371434/
當時寫完以後有同事朋友就提出了一些疑問,對於漢字在MySQL和Oracle中的存放情況希望我能夠詳細的說說。
關於MySQL中的varchar字元型別,自己的操作都是基於字符集UTF-8。
對於存放漢字,涉及到字元,位元組,編碼的一些知識,我查了一下,自己先補補,發現有一個帖子已經描述的很詳細了。直接引用過來。
http://www.regexlab.com/zh/encoding.htm
從編碼的發展來看,大致可以分為三個階段。系統內碼 說明 系統
階段一 ASCII 計算機剛開始只支援英語,其它語言不能夠在計算機上儲存和顯示。
階段二 ASCII(本地化) 為使計算機支援更多語言,通常使用 0x80~0xFF 範圍的 2 個位元組來表示 1 個字元。比如:漢字 '中' 在中文作業系統中,使用 [0xD6,0xD0] 這兩個位元組儲存。
然後不同的國家和地區制定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個位元組來代表一個字元的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文作業系統下,ANSI 編碼代表 JIS 編碼。
階段三 UNICODE(國際化) 為了使國際間資訊交流更加方便,國際組織制定了 UNICODE 字符集,為各種語言中的每一個字元設定了統一併且唯一的數字編號,以滿足跨語言、跨平臺進行文字轉換、處理的要求。
因為對Oracle中的一些細節略為熟悉,所以能夠旁敲側擊出MySQL中的一些相通的地方。
在編碼的基礎上,字元,位元組的關係就很重要了。
字元是一個抽象意義的符號,一個漢字或一個字母都是一個字元。
而位元組是計算機中儲存資料的單元,一個8位的二進位制數
如果對Oracle接觸長了,再用MySQL,一個很糾結的地方就是漢字的存放,在MySQL中,漢字和字母都是平等對待的,都是按照字元來存放的。
但是Oracle中卻不然,可以聚個簡單的例子。
SQL> create table test (name varchar2(6));
Table created.
SQL> insert into test values('123456');
1 row created.
SQL> insert into test values('一二三');
insert into test values('一二三')
*
ERROR at line 1:
ORA-12899: value too large for column "SYS"."TEST"."NAME" (actual: 9, maximum:6)
SQL> insert into test values('一二');
1 row created.
在Oracle中有一個dump函式能夠很清晰的檢視出資料的儲存情況。
比如下面的情況,檢視漢字和字母,每個漢字是按照3個位元組來存放的,每個字母則是一個位元組。
SQL> select dump('你好') from dual;
DUMP('你好')
-------------------------------------
Typ=96 Len=6: 228,189,160,229,165,189
SQL> select dump('a') from dual;
DUMP('A')
----------------
Typ=96 Len=1: 97
這一點和MySQL存在著明顯的差別,Oracle中其實也可以得到和MySQL同樣的效果。
這就涉及到一個資料庫引數NLS_LENGTH_SEMANTICS,這個引數用於指定CHAR列或VARCHAR2列的長度定義方式,預設值為BYTE。當設定該引數為BYTE時,定義CHAR列或VARCHAR2列採用位元組長度方式;當設定該引數為CHAR時,定義CHAR列或VARCHAR2列採用字元個數方式。
為了不傷筋動骨,我就在session級別做一些變更來說明這個問題。首先把它從byte變更為char(注意這個引數在oracle中是作為基本的初始化引數,一般不需要修改)
SQL>ALTER SESSION SET nls_length_semantics=char;
建立一個測試表,指定欄位長度為varchar2(6)
SQL> create table test_char (name varchar2(6));
Table created.
SQL> insert into test_char values('北京');
1 row created.
SQL> insert into test_char values('北京歡迎你'); --插入5個字元也沒有問題,情況和之前明顯不同。
1 row created.
SQL> insert into test_char values('北京歡迎你啊'); --插入6個字元也沒有問題。
1 row created.
SQL> insert into test_char values('北京歡迎你哈哈');
insert into test_char values('北京歡迎你哈')
*
ERROR at line 1:
ORA-12899: value too large for column "N1"."TEST_CHAR"."NAME" (actual: 8, maximum: 6)
然後我們為了對別插入兩組數字看看效果。
SQL> insert into test_char values('1234');
1 row created.
SQL> insert into test_char values(123456);
1 row created.
SQL> select name,dump(name) text from test_char;
NAME TEXT
------------------------ ----------------------------------------------------------------------------------------------------
北京 Typ=1 Len=6: 229,140,151,228,186,172
北京歡迎你 Typ=1 Len=15: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160
北京歡迎你啊 Typ=1 Len=18: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160,229,149,138
1234 Typ=1 Len=4: 49,50,51,52
123456 Typ=1 Len=6: 49,50,51,52,53,54
這樣來檢視好像和最開始的情況沒有任何的變化。可以看到還是按照三個位元組來存放,但是代表的意義已經發生了變化。
在MySQL中的情況和在oracle 引數nls_length_semantics=char;的情況是類似的。
SQL>create table test(name varchar(10));
SQL>insert into test values('1234567890');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values('一二三四五六七八九十');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values(null,12345678901);
ERROR 1406 (22001): Data too long for column 'name' at row 1
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 | --最後這個地方直接給truncate掉了多餘的部分。
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
這一點在資料遷移的時候如果不注意就是很嚴重的問題。不知道MySQL中是否也有和oracle中類似的dump函式,但是我們可以通過類似相通的部分來互相印證。
當時寫完以後有同事朋友就提出了一些疑問,對於漢字在MySQL和Oracle中的存放情況希望我能夠詳細的說說。
關於MySQL中的varchar字元型別,自己的操作都是基於字符集UTF-8。
對於存放漢字,涉及到字元,位元組,編碼的一些知識,我查了一下,自己先補補,發現有一個帖子已經描述的很詳細了。直接引用過來。
http://www.regexlab.com/zh/encoding.htm
從編碼的發展來看,大致可以分為三個階段。系統內碼 說明 系統
階段一 ASCII 計算機剛開始只支援英語,其它語言不能夠在計算機上儲存和顯示。
階段二 ASCII(本地化) 為使計算機支援更多語言,通常使用 0x80~0xFF 範圍的 2 個位元組來表示 1 個字元。比如:漢字 '中' 在中文作業系統中,使用 [0xD6,0xD0] 這兩個位元組儲存。
然後不同的國家和地區制定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個位元組來代表一個字元的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文作業系統下,ANSI 編碼代表 JIS 編碼。
階段三 UNICODE(國際化) 為了使國際間資訊交流更加方便,國際組織制定了 UNICODE 字符集,為各種語言中的每一個字元設定了統一併且唯一的數字編號,以滿足跨語言、跨平臺進行文字轉換、處理的要求。
因為對Oracle中的一些細節略為熟悉,所以能夠旁敲側擊出MySQL中的一些相通的地方。
在編碼的基礎上,字元,位元組的關係就很重要了。
字元是一個抽象意義的符號,一個漢字或一個字母都是一個字元。
而位元組是計算機中儲存資料的單元,一個8位的二進位制數
如果對Oracle接觸長了,再用MySQL,一個很糾結的地方就是漢字的存放,在MySQL中,漢字和字母都是平等對待的,都是按照字元來存放的。
但是Oracle中卻不然,可以聚個簡單的例子。
SQL> create table test (name varchar2(6));
Table created.
SQL> insert into test values('123456');
1 row created.
SQL> insert into test values('一二三');
insert into test values('一二三')
*
ERROR at line 1:
ORA-12899: value too large for column "SYS"."TEST"."NAME" (actual: 9, maximum:6)
SQL> insert into test values('一二');
1 row created.
在Oracle中有一個dump函式能夠很清晰的檢視出資料的儲存情況。
比如下面的情況,檢視漢字和字母,每個漢字是按照3個位元組來存放的,每個字母則是一個位元組。
SQL> select dump('你好') from dual;
DUMP('你好')
-------------------------------------
Typ=96 Len=6: 228,189,160,229,165,189
SQL> select dump('a') from dual;
DUMP('A')
----------------
Typ=96 Len=1: 97
這一點和MySQL存在著明顯的差別,Oracle中其實也可以得到和MySQL同樣的效果。
這就涉及到一個資料庫引數NLS_LENGTH_SEMANTICS,這個引數用於指定CHAR列或VARCHAR2列的長度定義方式,預設值為BYTE。當設定該引數為BYTE時,定義CHAR列或VARCHAR2列採用位元組長度方式;當設定該引數為CHAR時,定義CHAR列或VARCHAR2列採用字元個數方式。
為了不傷筋動骨,我就在session級別做一些變更來說明這個問題。首先把它從byte變更為char(注意這個引數在oracle中是作為基本的初始化引數,一般不需要修改)
SQL>ALTER SESSION SET nls_length_semantics=char;
建立一個測試表,指定欄位長度為varchar2(6)
SQL> create table test_char (name varchar2(6));
Table created.
SQL> insert into test_char values('北京');
1 row created.
SQL> insert into test_char values('北京歡迎你'); --插入5個字元也沒有問題,情況和之前明顯不同。
1 row created.
SQL> insert into test_char values('北京歡迎你啊'); --插入6個字元也沒有問題。
1 row created.
SQL> insert into test_char values('北京歡迎你哈哈');
insert into test_char values('北京歡迎你哈')
*
ERROR at line 1:
ORA-12899: value too large for column "N1"."TEST_CHAR"."NAME" (actual: 8, maximum: 6)
然後我們為了對別插入兩組數字看看效果。
SQL> insert into test_char values('1234');
1 row created.
SQL> insert into test_char values(123456);
1 row created.
SQL> select name,dump(name) text from test_char;
NAME TEXT
------------------------ ----------------------------------------------------------------------------------------------------
北京 Typ=1 Len=6: 229,140,151,228,186,172
北京歡迎你 Typ=1 Len=15: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160
北京歡迎你啊 Typ=1 Len=18: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160,229,149,138
1234 Typ=1 Len=4: 49,50,51,52
123456 Typ=1 Len=6: 49,50,51,52,53,54
這樣來檢視好像和最開始的情況沒有任何的變化。可以看到還是按照三個位元組來存放,但是代表的意義已經發生了變化。
在MySQL中的情況和在oracle 引數nls_length_semantics=char;的情況是類似的。
SQL>create table test(name varchar(10));
SQL>insert into test values('1234567890');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values('一二三四五六七八九十');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values(null,12345678901);
ERROR 1406 (22001): Data too long for column 'name' at row 1
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 | --最後這個地方直接給truncate掉了多餘的部分。
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
這一點在資料遷移的時候如果不注意就是很嚴重的問題。不知道MySQL中是否也有和oracle中類似的dump函式,但是我們可以通過類似相通的部分來互相印證。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-1434762/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle實驗(01):字元 & 位元組Oracle字元
- MySQL 中字元編碼問題MySql字元
- Oracle 通過透明閘道器訪問mysqlOracleMySql
- 解決 requests 庫中的位元組物件問題物件
- 面試必問的 Java 位元組流與字元流面試Java字元
- Java的位元組流,字元流和緩衝流對比探究Java字元
- 通過位元組碼看原理,帶你去找kotlin中的static方法Kotlin
- Python字元與位元組新編Python字元
- [BUG反饋]新增模組無法通過URL訪問的問題
- java入門(字元流、位元組流)Java字元
- 位(bit/b)、位元組(byte/B)、字元字元
- 【oracle 資料匯入匯出字元問題】Oracle字元
- 通過git bisect快速定位大型工程中的問題Git
- 正規表示式匹配雙位元組字元字元
- ORACLE RAC TO RAC DG搭建過程中可能遇到的問題Oracle
- [Oracle-> MySQL] Oracle通過dblink連線MySQL--Oracle 19c連線到MySQL 5.7OracleMySql
- 開發過程中mysql常見問題MySql
- 通過幾個問題深入分析Vue中的keyVue
- Modbus通訊協議中的四種位元組順序協議
- 得到字串 位元組 長度 中文 兩個字元 英文一個字元字串字元
- 教育類app開發過程中,需要避免的問題APP
- Dalvik 和 Java 位元組碼的比較Java
- Db2 位元組問題&Db2-4229 錯誤問題DB2
- SpringBoot中通過CORS解決跨域問題Spring BootCORS跨域
- ORACLE通過透明閘道器建dblink連線Postgresql的幾個問題OracleSQL
- Oracle/MySQL透過odbc訪問PostgreSQL for LightDBOracleMySql
- Laravel對不同版本的MySQL字元編碼報錯問題LaravelMySql字元
- 通過實體類生成 mysql 的建表語句MySql
- 位元組跳動面試官這樣問有關字串的問題!!面試字串
- Python原始檔一行字元過長造成的問題Python字元
- Java類轉位元組碼工具Java
- Oracle中文漢字佔用位元組Oracle
- utf-8字元所佔位元組數字元
- JavaIO流(一)-位元組輸入流與字元輸入流JavaAI字元
- Java的位元組碼和ABAP load的比較Java
- 關於mysql字元和數字型別轉換的問題研究MySql字元型別
- mysql 分組取每個組的前幾名的問題MySql
- Oracle 透過透明閘道器 訪問 mysqlOracleMySql
- 用抓包工具本地檢視位元組序問題!!