1. 程式人生 > 程式設計 >2-MySQL的資料型別

2-MySQL的資料型別

前言

MySQL的資料型別分為五大類: 數值型別、字串型別、時間型別、空間型別和json型別(5.7新增)。每個大類下面又有很多具體型別,它們的不同主要體現在長度範圍、精度和需要的物理空間(磁碟和記憶體)。

注: 本文講述基於MySQL5.7,有些內容5.7 版本以下的可能不適用,如json型別

數值型別

數值型別只是個大類,它下面還有BIT、INT、FLOAT、DECIMAL等具體分類。

MySQL對數值型別的定義有如下約定:
1.符號[]表示裡面的引數是可選的

2.對於整數型別,M表示該型別顯示的最大長度,與實際儲存的範圍無關;對於實數型別(Float、Double、Decimal),M表示該型別儲存的最大長度

3.對於整數型別,有SIGNED和UNSIGNED兩種屬性,預設屬性為SIGNED,能夠儲存負數。如果屬性為UNSIGNED,則不允許儲存負數。如果沒有儲存負數的需求,UNSIGNED屬性的型別可以使儲存的範圍提高一倍。比如TINYINT的儲存範圍是 -128-127,而UNSIGNED TINYINT 的儲存範圍是0-255。

4.如果定義一個型別時指定了ZEROFILL屬性,則該型別預設帶有UNSIGNED屬性,即不能儲存負數

5.“SERIAL” 型別是 “BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE” 的別名。

6.“serial default value“ 是 ”NOT NULL AUTO_INCREMENT UNIQUE” 的別名

# 實驗1
create table mysql_test_serial (
    id int(11) not null primary key,single_serial serial -- 由於主鍵id沒有指定自增,所以允許新增
);

create table mysql_test_serial2 (
    id int(11) not null primary key,serial_attribute int(11) serial default value,-- 由於主鍵id沒有指定自增,所以允許新增
    zerofill_attribute int(11) zerofill not null default 0,-- 測試是否預設帶有unsigned屬性
    unsigned_attribute int(11) unsigned not null default 0 -- 定義帶有unsigned屬性的型別
);
複製程式碼

實驗1效果圖:

BIT[(M)]

位資料型別,M表示儲存的值的位數,範圍從1-64,預設值是1

# 實驗2: 注意位資料型別的加減,需要謹慎使用
create table bittest (
    a bit(8)
)

insert into bittest set a=b'00111001';

# a是ASCII碼為57的字元“9”, 但是a+0的數字加減場景中,得到的是57。
mysql> select a,a+0 from bittest;
+------+------+
| a    | a+0  |
+------+------+
| 9    |   57 |
+------+------+
複製程式碼

BOOL/BOOLEAN

TINYINT(1)型別的別名,它有兩個值,TRUE和FALSE。

該型別的邏輯判斷方式跟常見的不太一樣,詳情參見:dev.mysql.com/doc/refman/…

INT(整數)

儲存的值為整數,包含五個子類:TINYINT / SMALLINT / INT / MEDIUMINT / BIGINT, 儲存的範圍見下圖:

INT(11) 表示的含義:
前面講過,如果是整數型別,括號裡面的11,只表示該型別顯示的最大長度,與實際儲存的範圍無關,可通過實驗3證明。

# 實驗3
# 建立表(必須加上zerofill,才能看到有沒有補零)
create table test_int_length (
    id int(11) not null primary key,int_four1 int(4) not null default 0,int_four2 int(4) zerofill not null default 0,int_eleven int(11) zerofill not null default 0
);

# 插入資料
insert into test_int_length (id,int_four1,int_four2) values (1,1,1);
insert into test_int_length (id,int_four2) values (2,100000,100000);

# 可以看到當插入資料為1, 不足4位,會自動補零
mysql> select * from test_int_length;
+----+-----------+-----------+-------------+
| id | int_four1 | int_four2 | int_eleven  |
+----+-----------+-----------+-------------+
|  1 |         1 |      0001 | 00000000000 |
|  2 |    100000 |    100000 | 00000000000 |
+----+-----------+-----------+-------------+
2 rows in set (0.00 sec)

複製程式碼

DECIMAL[(M[,D])]

MySQL提供兩種型別(FLOAT和DOUBLE)來儲存浮點數(小數),當儲存的型別在規定的範圍內時,這兩種型別儲存的值是精確的,但如果超過規定的範圍,則超過的部分會被截斷和四捨五入,導致精度缺失。

為了使儲存的浮點數不會丟失精度,MySQL提供了DECIMAL這種型別。其中,M表示總位數(不包括小數點和負數前面的橫槓),D表示小數點後的位數。

M最大值為65,預設值是10; D最大值是30,預設值是0

DEC VS NUMERIC VS FIXED
可能有時候我們會看到這三種型別,其實這三種型別都是DECIMAL的別名。

注: 除了可以用DECIMAL型別來保證儲存的小數是精確的,還有另外一種取巧的辦法,使用整數型別儲存,在最終顯示的時候除以對應的值即可,比如1.777 = 1777 / 1000。

FLOAT[(M,D)]

單精度浮點型。儲存的範圍為(-3.402823466E+38,-1.175494351E-38),和 (1.175494351E-38 to 3.402823466E+38),因為不可能儲存無限位小數,所以只能無限接近於0。

M是總位數,D是小數點後的位數。如果省略M和D,則將值儲存到硬體允許的極限。單精度浮點數的精度約為小數點後7位。

FLOAT(p)
一種特殊的表現形式,當p的範圍是0-24時,表示使用FLOAT型別;當p的範圍是25-63時,表示使用DOUBLE型別。

DOUBLE[(M,D)]

單精度浮點型。儲存的範圍為(-1.7976931348623157E+308,-2.2250738585072014E-308),和 (2.2250738585072014E-308 to 1.7976931348623157E+308)

M是總位數,D是小數點後的位數。如果省略M和D,則將值儲存到硬體允許的極限。單精度浮點數的精度約為小數點後15位。

REAL / DOUBLE PRECISION
這兩種型別是DOUBLE型別的別名,不過如果“REAL_AS_FLOAT”這個引數設定為enabled,REAL代表的是FLOAT型別。

時間型別

MySQL的時間型別包含五個具體型別: DATE,TIME,DATETIME,TIMESTAMP,and YEAR。其中,TIME、TIMESTAMP、DATETIME允許精確到微妙,即小數點後6位。

五種型別的儲存空間:

fractional seconds strorage表示小數點後的數字佔用的位元組

圖片名稱

舉例: TIME(0),TIME(2),TIME(4) 和 TIME(6) 分別佔用 3,4,5,and 6 bytes。

DATE

支援的範圍 '1000-01-01' - '9999-12-31'。雖然DATE型別使用'YYYY-MM-DD'格式展示,但是它支援數值或者字串的輸入方式,舉例如下。

# 建表
create table datetest (
    date1 DATE,datetime1 DATETIME,datetime2 DATETIME(3),);
# 插入資料(數值或者字串)
mysql> insert into datetest set date1=20191128;
mysql> insert into datetest set date1='20191128';
mysql> insert into datetest set date1='2019-11-28';

# 插入當前時間
mysql> insert into datetest set date1=CURDATE();
複製程式碼

DATETIME[(fsp)]

支援的範圍 '1000-01-01 00:00:00.000000' - '9999-12-31 23:59:59.999999',預設值是NULL,同樣也支援數值或者字串的輸入方式,舉例如下。

由於DATETIME沒有時區的概念,預設時間是UTC時間,如果想要改為北京時間,則需要修改time_zone引數

檢視:

show global variables like '%time_zone%';

修改:
blog.csdn.net/iris_xuting…

實驗:

create table datetest (
    date1 DATE,);

# 插入資料(數值或者字串)
mysql> insert into datetest set datetime1='2019-11-28 12:00:00';
mysql> insert into datetest set datetime1=20191128120000;

# 插入當前時間
mysql> insert into datetest set datetime1=CURDATE();

# datetime和timestamp都可以設定自動初始化和更新時間
create table datetest2 (
    datetime1 DATETIME DEFAULT CURRENT_TIMESTAMP,-- 只是自動初始化,當資料改變時時間不會改變
    datetime2 DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,-- 當資料改變時時間會隨之改變
    datetime3 DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)  -- 當指定精度時,DEFAULT 和 ON UPDATE也要指定相同精度
);

複製程式碼

TIMESTAMP[(fsp)]

支援的範圍 '1970-01-01 00:00:01.000000' - '2038-01-19 03:14:07.999999',這裡的時間範圍基於UTC時間。另外,TIMESTAMP列的預設值不能為NULL

需要注意的是,TIMESTAMP的表現形式基於 explicit_defaults_for_timestamp 引數,預設值是OFF(5.7版本),不開啟。

如果該引數為ON,則型別為TIMESTAMP的column不會自動初始化和更新時間。

如果該引數為OFF且有多個列的型別都為TIMESTAMP,只有第一個列的時間會自動更新,其他列不會(除非加上 DEFAULT 和 ON UPDATE)

# 建立表
create table datetest3 (
    id int(11) not null default 0,timestamp1 TIMESTAMP,timestamp2 TIMESTAMP not null default '2018-10-01 00:00:00'
);

# 測試
mysql> insert into datetest3 set id=1;

mysql> select * from datetest3;
+----+---------------------+---------------------+
| id | timestamp1          | timestamp2          |
+----+---------------------+---------------------+
|  1 | 2019-11-30 12:30:58 | 2018-10-01 00:00:00 |
+----+---------------------+---------------------+
複製程式碼

TIMESTAMP的時間範圍為什麼只到2038?
www.jianshu.com/p/b222dcc47…

TIMESTAMP的時區問題
www.cnblogs.com/gaogao67/p/…
www.cnblogs.com/deverz/p/98…
blog.csdn.net/sinat_26594…
segmentfault.com/a/119000001…

注:對於有夏令時的地區,本地時間等於UTC時間 + 時區偏移 + 夏令時偏移

TIME[(fsp)]

支援的範圍 '-838:59:59.000000' - '838:59:59.000000',這種型別一般比較少用,除非你只需要當天的時間。

YEAR[(4)]

儲存年份,一般也比較少用,除非你只需要儲存年份。

MySQL之前的版本還支援YEAR(2)型別,不過這個型別將在MySQL5.7.5中廢棄

字串型別

MySQL的字串型別包含以下具體型別: CHAR,VARCHAR,TEXT,ENUM和SET。

特性:
1.可以單獨對型別為字串的列設定字符集(CHARSET / CHARACTER SET)

CREATE TABLE t
(
    c1 VARCHAR(20) CHARSET utf8,c2 TEXT CHARACTER SET latin1 COLLATE latin1_general_cs
);
複製程式碼

2.可以對整張表設定字符集

CREATE TABLE t2
(
    c1 VARCHAR(20) CHARSET utf8,c2 TEXT
) CHARSET utf8mb4;

mysql> show full columns from t2;
+-------+-------------+--------------------+------+-----+---------+
| Field | Type        | Collation          | Null | Key | Default |
+-------+-------------+--------------------+------+-----+---------+
| c1    | varchar(20) | utf8_general_ci    | YES  |     | NULL    |
| c2    | text        | utf8mb4_general_ci | YES  |     | NULL    |
+-------+-------------+--------------------+------+-----+---------+
複製程式碼

3.字符集別名
ASCII ==> CHARACTER SET latin1
UNICODE ==> CHARACTER SET ucs2

4.二進位制型別 VS 文字型別
blog.csdn.net/u014465934/…

CHAR[(M)]

該型別儲存的是定長字串,與數值型別不同,M表示儲存的最大字元數, M的預設值為1,最大值為255。對於utf8字符集來說,一個字母和一個漢字都算是一個字元

注: 這裡要注意儲存字元(characters)和儲存位元組(bytes)的區別,比如CHAR(5),它最大儲存5個字元,如果使用的是utf8字符集,儲存的位元組是5 * 3位元組=15位元組。

create table char_test (
    id int(11) not null default 0,char1 char(10) charset utf8 not null default ''
);

-- 無法插入,超出儲存範圍
insert into char_test set char1="abcdefghijkl";
insert into char_test set char1="一二三四五六七八九十十一";

-- 可以插入
insert into char_test set char1="abcdefghij";
insert into char_test set char1="一二三四五六七八九十";

複製程式碼

注: NCHAR、NATIONAL CHARACTER 是 CHAR CHARACTER SET utf8 的別名

VARCHAR(M)

該型別儲存的是非定長字串,即有可能用1位元組,2位元組或者3位元組等儲存一個字元。M也表示儲存的最大字元數,M的範圍是0-65535。

這裡也要注意下位元組和字元的區別,同CHAR。

VARCHAR(255)的由來
1.雖然VARCHAR最大能夠儲存65535個字元,但檔案同時也提到MySQL表的每一行也是有大小限制的,非常巧,這個數字是65535位元組

2.通過計算,如果varchar使用的是utf8字符集,則每一行儲存的最大字元數為65535/3=21845; 如果使用utf8mb4,則最大字元數為65535/4=16383。

但上面的計算方式其實是一種理想化的計算,實際上還要減去一些額外的位元組,有兩種計算方式: 1.如果包含一個欄位id,且型別為int,則儲存的字元為(65535-1-2-4)/3 = 21843 字元。

2.如果不包含欄位id,則儲存的字元為(65535-1-2)/3 = 21844 字元。

  • 減1的原因是實際行的儲存從第2個位元組開始.
  • 減2的原因是varchar頭部的2個位元組表示長度.
  • 除以3的原因是一個utf8字元佔用3個位元組.(如果是utf8mb4要除以4)

那是不是就可以定義varchar(21844)呢? 確實可以,但是這樣不現實,因為你不只這一列,其他列也需要佔用位元組。

3.varchar(255)的由來是因為InnoDB儲存引擎的表索引的字首長度最長是767位元組(bytes),起因是2^8×3-1。767表示3個字元最大佔用空間(utf8)。

所以,如果需要建索引,就不能超過 767 bytes,utf8編碼時 255*3=765bytes,恰恰是能建索引情況下的最大值。

總而言之,255是一個是保證能少出錯的一個很好的預設值

# 實驗
mysql> create table varchar_test (
    ->     id int(11) not null default 0,->     varchar1 varchar(21843) not null default ''
    -> ) charset utf8;
Query OK,0 rows affected (0.01 sec)

mysql> create table varchar_test1 (
    ->     id int(11) not null default 0,->     varchar1 varchar(21844) not null default ''
    -> ) charset utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,not counting BLOBs,is 65535. This includes storage overhead,check the manual. You have to change some columns to TEXT or BLOBs

mysql> create table varchar_test3 (
    ->     varchar1 varchar(21844) not null default ''
    -> ) charset utf8;
Query OK,0 rows affected (0.01 sec

複製程式碼


CHAR VS VARCHAR

  • 1.CHAR儲存的是定長字串,VARCHAR儲存的是不定長字串,假設使用的utf8字符集,則CHAR型別的所有值都用三個位元組儲存,而VARCHAR有可能使用1位元組,2位元組,3位元組,具體看儲存的字串,所以相對來說,VARCHAR更省磁碟空間
  • 2.如果字串尾部帶有空格,CHAR型別預設會去掉,而VARCHAR型別不會。

utf8 VS utf8mb4

  • 1.MySQL的utf8字符集並不等同廣義的UTF-8字符集,utf8mb4才是
  • 2.utf8字符集每個字元最多使用三個位元組,utf8mb4 字符集每個字元最多使用四個位元組
  • 3.utf8mb4 是utf8的超集,包含其缺失的一些字元

BINARY[(M)]

與CHAR型別相似,但儲存的是二進位制資料(二、八、十六進位制也可以),M表示儲存的最大位元組,預設為1

VARBINARY(M)

與VARCHAR型別相似,但儲存的是二進位制資料,M表示儲存的最大位元組。

BLOB

該型別主要用於儲存比較大的二進位制檔案,包含四個具體型別: TINYBLOB / BLOB / MEDIUMBLOB / LONGBLOB。

TEXT

該型別主要用於儲存比較大的文字,包含四個具體型別: TINYTEXT / TEXT / MEDIUMTEXT / LONGTEXT。

BLOB和TEXT儲存佔用的位元組(L表示可儲存的位元組,實際佔用的位元組比L大,因為額外的位元組被用於表示長度)

注:由於型別較多,所以只講述了比較重要的型別,json型別有精力會繼續補充!(本文未經允許,不可轉載!)

參考:
segmentfault.com/q/101000000…

www.dazhuanlan.com/2019/10/15/…

m.w3cschool.cn/mysql/mysql…

dev.mysql.com/doc/refman/…

dev.mysql.com/doc/refman/…