1、前言
對於從事互聯網開發的同窗來講,mysql可謂是再熟悉不過的了。不管是DBA、開發或測試,基本上每天要跟它打交道,不少同窗可能已經身經百戰了。可是,筆者遇到過的這些坑不知道大家都經歷過沒?mysql
2、有符號和無符號
之前咱們公司在項目開發之初制定開發規範時,對mysql的int類型字段定義成有符號,仍是無符號問題專門討論過。sql
觀點一:數據庫
對於可以肯定裏面存的值必定是正數的字段,定義成UNSIGNED無符號的,能夠節省一半的存儲空間。建立無符號字段的語句以下:函數
create table test_unsigned(a int UNSIGNED, b int UNSIGNED);
觀點二:測試
建議都定義成有符號的,使用起來比較簡單,mysql默認int類型就是有符號的,建立有符號字段的語句以下:編碼
create table test_signed(a int); insert into test_signed values(-1);
執行結果:spa
在字段a中插入-1,咱們看到是能夠操做成功的。server
這兩個方案,通過咱們激烈討論以後,選擇了使用有符號定義int類型字段。爲何呢?blog
create table test_unsigned(a int UNSIGNED, b int UNSIGNED); insert into test_unsigned values(1,2);
先建立test_unsigned表,裏面包含兩個無符號字段a和b,再插入一條數據a=1,b=2排序
select b - a from test_unsigned;
沒有問題,返回1
可是若是sql改爲這樣:
select a - b from test_unsigned;
執行結果:
報錯了。。。
因此,在使用無符號字段時,千萬要注意字段相減出現負數的坑,建議仍是使用有符號字段,避免沒必要要的問題。
3、自動增加
建過表的同窗都知道,對於表的主鍵能夠定義成自動增加的,這樣一來,就能夠交給數據庫本身生成主鍵值,而無需在代碼中指定,並且生成的值是遞增的。通常狀況下,建立表的語句以下:
create table test_auto_increment (a int auto_increment primary key);
但若是改爲這樣的會怎樣?
create table test_auto_increment (a int auto_increment);
執行結果:
報錯了。。。
截圖中沒有所有顯示出來,完整的提示語是這樣的:
1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key, Time: 0.006000
意思是自動增加字段,必須被定義成key,因此咱們須要加上primary key。
此外,還有一個有趣的實驗:
insert into test_auto_increment(a) values (null),(50),(null),(8),(null);
你們猜猜執行結果會是什麼樣的?
第一個null插入1,而後按真實的數字大小排序後插入,後面兩個null,是在最大的數字上面加1。
再看看這條sql主鍵中插入負數,能執行成功嗎?
insert into test_auto_increment values(-3);
答案是能夠,主鍵能夠插入負數。
還有這條sql呢,主鍵中插入0?
insert into test_auto_increment values(0);
執行結果:
也能夠執行成功,可是沒有插入數據
4、字段長度
咱們在建立表的時候,給字段定義完類型以後,緊接着須要指定字段的長度,好比:varchar(20),biginit(20)等。那麼問題來了,varchar表明的是字節長度,仍是字符長度呢?
create table test_varchar(a varchar(20)); insert into test_varchar values('蘇三說技術'); select length(a),CHARACTER_LENGTH(a) from test_varchar;
執行後的結果:
咱們看到中文的5個字length函數統計後長度爲15,表明佔用了15個字節,而使用charcter_length函數統計長度是5,表明有5個字符。因此varchar表明的是字符長度,由於有些複雜的字符或者中文,一個字節表示不了,utf8編碼格式的一箇中文漢字佔用3個字節。不一樣的數據庫編碼格式,佔用不一樣的字節數對照表以下:
mysql除了varchar和char是表明字符長度以外,其他的類型都是表明字節長度。
int(n) 這個n表示什麼意思呢?從一個列子出發:
create table test_bigint (a bigint(4) ZEROFILL); insert into test_bigint values(1); insert into test_bigint values(123456); select * from test_bigint;
ZEROFILL表示長度不夠填充0
執行結果:
mysql經常使用數字類型字段佔用字節數對照表:
從表中能夠看出bigint實際長度是8個字節,可是咱們定義的a顯示4個字節,因此在不滿4個字節時前面填充0。滿了4個字節時,按照實際的長度顯示,好比:123456。可是,須要注意的是,有些mysql客戶端即便滿了4個字節,也可能只顯示4個字節的內容,好比顯示:1234。
因此bigint(4),這裏的4表示顯示的長度爲4個字節,實際長度仍是8個字節。
5、忽略大小寫
咱們知道在英文字母中有大小寫問題,好比:a 和 A 是同樣的嗎?咱們認爲確定是不同的,可是數據庫是如何處理的呢?
create table test_a(a varchar(20)); insert into test_a values('a'); insert into test_a values('A'); select * from test_a where a = 'a';
執行結果是什麼呢?
本覺得只會返回a,可是實際上把A也返回了,這是爲何呢?
該表默認的Collation是utf8_general_ci,這種Collation會忽略大小寫,因此纔會出現查詢小寫字母a的值,意外把大寫字母A的值也查詢出來了。
那麼若是咱們只想查詢出小寫a的值該怎麼辦?先看看mysql支持哪些Collation?
show collation;
從上圖中咱們能夠找到utf8_bin,這個表示二進制格式的數據,咱們設置成種類型的試試。
修改一下字段類型
ALTER TABLE test_a MODIFY COLUMN a VARCHAR(20) BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;
再查看一下數據
select * from test_a where a = 'a';
執行結果:
果真,結果對了。
6、特殊字符
筆者以前作項目的時候,提供過一個留言的功能,結果客戶端用戶輸入了一個emoji表情,直接致使接口報錯了。
最後定位緣由是因爲當時數據庫和表的字符編碼都是用的utf8,mysql的utf8編碼的一個字符最多3個字節,可是一個emoji表情爲4個字節,因此utf8不支持存儲emoji表情。
該如何解決這個問題呢?
將字符編碼改爲utf8mb4,utf8mb4最多能有4字節,不過,在mysql5.5.3或更高的版本才支持。
在mysql 的配置文件 my.cnf 或 my.ini 配置文件中修改以下:
[client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_general_ci
重啓MySQL,而後使用如下命令查看編碼,應該所有爲utf8mb4,這是修改整個數據庫的編碼方式。
SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';ji
結果爲:
也能夠單獨修改某張表的編碼方式:
alter table test_a convert to character set utf8mb4 collate utf8mb4_bin;
以及修改某個字段的編碼方式:
ALTER TABLE test_a CHANGE a a VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
此外,建議同窗們在建立數據庫和表的時候字符編碼都定義成utf8mb4,避免一些沒必要要的問題。