溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL數(shù)據類型和存儲機制的示例分析

發(fā)布時間:2021-07-30 10:57:47 來源:億速云 閱讀:139 作者:小新 欄目:MySQL數(shù)據庫

小編給大家分享一下MySQL數(shù)據類型和存儲機制的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

1.1 數(shù)據類型概覽

數(shù)據類型算是一種字段約束,它限制每個字段能存儲什么樣的數(shù)據、能存儲多少數(shù)據、能存儲的格式等。MySQL/MariaDB大致有5類數(shù)據類型,分別是:整形、浮點型、字符串類型、日期時間型以及特殊的ENUM和SET類型。

這5種數(shù)據類型的意義、限制和相關說明如下圖所示:

MySQL數(shù)據類型和存儲機制的示例分析

各數(shù)據類型占用字節(jié)數(shù),參見mariadb官方手冊。

1.2 存儲機制和操作方式

數(shù)據類型之所以能限定字段的數(shù)據存儲長度,是因為在創(chuàng)建表時在內存中嚴格劃定了地址空間,地址空間的長度是多少就能存儲多少字節(jié)的數(shù)據。當然,這是一個很粗獷的概念,更具體的存儲方式見下面的描述。

數(shù)據類型限定范圍的方式有兩種:一是嚴格限定空間,劃分了多少空間就只能存儲多少數(shù)據,超出的數(shù)據將被切斷;二是使用額外的字節(jié)的bit位來標記某個地址空間的字節(jié)是否存儲了數(shù)據,存儲了就進行標記,不存儲就不標記。

1.2.1 整型的存儲方式

此處主要說明整型的存儲方式,至于浮點型數(shù)據類型的存儲方式要考慮的東西太多。

對于整型數(shù)據類型來說,它嚴格限定空間,但它和字符不同,因為每個已劃分的字節(jié)上的bit位上的0和1直接可以計算出數(shù)值,所以它的范圍是根據bit位的數(shù)量值來計算的。一個字節(jié)有8個Bit位,這8個bit位可以構成2^8=256個數(shù)值,同理2字節(jié)的共2^16=65536個數(shù)值,4字節(jié)的int占用32bit,可以表示的范圍為0-2^32。也就是說,在0-255之間的數(shù)字都只占用一個字節(jié),256-65535之間的數(shù)字需要占用兩個字節(jié)。

需要注意,在MySQL/mariadb中的整型數(shù)據類型可以使用參數(shù)M,M是一個正整數(shù),例如INT(M),tinyint(M)。這個M表示的是顯示長度,如int(4)表示在輸出時將顯示4位整數(shù),如果實際值的位數(shù)小于顯示值寬度,則默認使用空格填充在左邊。而結果位數(shù)超出時將不影響顯示結果。一般該功能都會配合zerofill屬性用0代替空格填充,但是使用了zerofill后,該列就會自動變成無符號字段。例如:

CREATE TABLE test3(id INT(2) ZEROFILL NOT NULL);
INSERT INTO test3 VALUES(1),(2),(11),(111);
SELECT id FROM test3;
+-----+
| id |
+-----+
| 01 |
| 02 |
| 11 |
| 111 |
+-----+
4 rows in set (0.00 sec)

唯一需要注意的是,顯示寬度僅僅影響顯示效果,不影響存儲、比較、長度計算等等任何操作。

1.2.2 字符類型的存儲方式

此處主要說明char和varchar的存儲方式以及區(qū)別。

char類型是常被稱為"定長字符串類型",它嚴格限定空間長度,但它限定的是字符數(shù),而非字節(jié)數(shù),但以前老版本中限定的是字節(jié)數(shù)。因此char(M)嚴格存儲M個字符,不足部分使用空格補齊,超出M個字符的部分直接截斷。

由于char類型有"短了就使用空格補足"的能力,因此為了體現(xiàn)數(shù)據的真實性,在從地址空間中檢索數(shù)據時將自動刪除尾隨的空格部分。這正是char的一個特殊性,即使是我們手動存儲的尾隨空格也會被認為是自動補足的,于是在檢索時被刪除。也就是說在where語句中name='gaoxiaofang '和name='gaoxiaofang'的結果是一樣的。

例如:

create table test2(a char(4) charset utf8mb4);
insert into test2 values('恭喜你'),('恭喜你成功晉級'),('hello'),('he ');
select concat(a,'x') from test2;
+---------------+
| concat(a,'x') |
+---------------+
| 恭喜你x |
| 恭喜你成x |
| hellx  |
| hex  |
+---------------+
4 rows in set

從上面的結果可以看到,char(4)只能存儲4個字符,并刪除尾隨空格。

varchar常被稱為"變長字符串類型",它存儲數(shù)據時使用額外的字節(jié)的bit位來標記某個字節(jié)是否存儲了數(shù)據。每存儲一個字節(jié)(不是字符)占用一個bit位進行記錄,因此一個額外的字節(jié)可以標記共256個字節(jié),2個額外的字節(jié)可以標記65536個字節(jié)。但MySQL/mariadb限制了最大能存儲65536個字節(jié)。這表示,如果是單字節(jié)的字符,它最多能存儲65536個字符,如果是多字節(jié)字符,如UTF8的每個字符占用3個字節(jié),它最多能存儲65536/3=21845個utf8字符。

因此,varchar(M)存儲時除了真實數(shù)據占用空間長度,還要額外計算1或2個字節(jié)的Bit位長度,即對于單字節(jié)字符實際占用的空間為M+1或M+2個字節(jié),對于多字節(jié)字符(如3字節(jié))實際占用的空間為M*3+1或M*3+2個字節(jié)。

由于varchar存儲時需要采用額外的bit位記錄每一個字節(jié),短了的數(shù)據不會自動使用補齊,因此顯式存儲的尾隨空格也會被存儲并在Bit位上進行標記,也就是說不會刪除尾隨空格。

和char(M)一樣,當指定varchar(2)時,只能存儲兩個字節(jié)的字符,如果超出了,則切斷。

關于char、varchar以及text字符串類型,它們在比較時不會考慮尾隨空格,但做like匹配或正則匹配時會考慮空格,因為匹配時字符是精確的。例如:

create table test4(a char(4),b varchar(5));
insert into test4 values('ab ','ab ');
select a='ab ',b='ab ',a=b from test4;
+-----------+--------------+-----+
| a='ab ' | b='ab ' | a=b |
+-----------+--------------+-----+
|  1 |  1 | 1 |
+-----------+--------------+-----+
1 row in set
select a like 'ab ' from test4;
+-------------------+
| a like 'ab ' |
+-------------------+
|   0 |
+-------------------+
1 row in set

最后需要說明的是,數(shù)值在存儲(或調入內存)時,以數(shù)值型方式存儲比字符型或日期時間類型更節(jié)省空間。因為整數(shù)值存儲時是直接通過bit計算數(shù)值的,0-255之間的任意整數(shù)都只占一個字節(jié),256-65535之間的任意整數(shù)都占2個字節(jié),而占用4個字節(jié)時便可以代表幾十億個整數(shù)之間的任意一個,這顯然比字符型存儲時每個字符占用一個字節(jié)節(jié)省空間的多。例如值"100"存儲為字符型時占用三個字節(jié),而存儲為數(shù)值型將只占用一個字節(jié)。因此數(shù)據庫默認將不使用引號包圍的值當作數(shù)值型,如果明確要存儲為字符型或日期時間型則應該使用引號包圍以避免歧義。

1.2.3 日期時間型的存儲方式

日期時間性數(shù)據存儲時需要使用引號包圍,避免和數(shù)值類型的數(shù)據產生歧義。關于日期時間的輸入方式是非常寬松的,以下幾種方式都是被允許的:任意允許的分隔符,建議使用4位的年份。

20110101
2011-01-01 18:40:20
2011/01/01 18-40-20
20110101184020

1.2.4 ENUM數(shù)據類型

ENUM數(shù)據類型是枚舉型。定義方式為ENUM('value1','value2','value3',...),在向該類型的字段中插入數(shù)據時只能插入value中的某一個或NULL,插入其他值或空(即'')時都將截斷為空數(shù)據。存儲時會忽略大小寫(將轉換為ENUM中的字符),且會截斷尾隨空格。

mysql> create table test6(id int auto_increment primary key,name char(20),gender enum('Mail','f'));
mysql> insert into test6(name,gender) values('malongshuai','Mail'),('gaoxiaofang','F'),('wugui','x'),('tuner',null),('woniu','');
Query OK, 5 rows affected
Records: 5 Duplicates: 0 Warnings: 2
mysql> show warnings;
+---------+------+---------------------------------------------+
| Level | Code | Message     |
+---------+------+---------------------------------------------+
| Warning | 1265 | Data truncated for column 'gender' at row 3 |
| Warning | 1265 | Data truncated for column 'gender' at row 5 |
+---------+------+---------------------------------------------+
2 rows in set
mysql> select * from test6;
+----+-------------+--------+
| id | name | gender |
+----+-------------+--------+
| 1 | malongshuai | Mail |
| 2 | gaoxiaofang | f |
| 3 | wugui | |
| 4 | tuner | NULL |
| 5 | woniu | |
+----+-------------+--------+
5 rows in set

ENUM類型的數(shù)據存儲時是通過index數(shù)值進行存儲的,相比于字符串類型,它只需要1或2個字節(jié)進行存儲即可。理論上,當value的數(shù)量少于256個時只需一個字節(jié),超出256個但少于65536個時使用2個字節(jié)存儲。MySQL/MariaDB限制最多只能存儲65536個value。當然,這是理論上的限制,實際存儲時要考慮的因素有很多,例如NULL也會占用bit位,所以實際存儲時可能250個value就需要2個字節(jié)。

ENUM的每個value都通過index號碼進行編號,無論是檢索還是操作該字段時都會通過index的值來操作。value1的index=1,value2的index=2,依次類推。但需要注意有兩個特殊的index值:NULL值的index=NULL,空數(shù)據的index=0。

例如ENUM('a','b','c'),向該字段依次插入'','b','a','c',NULL,'xxx'時,由于第一個和最后一個都會截斷為空數(shù)據,所以它們的index為0,插入的NULL的index為NULL,插入的'b','a','c'的index值分別為2,1,3。所以index號碼和值的對應關系為:

indexvalue
NULLNULL
0''
0''
1'a'
2'b'
3'c'

使用ENUM的index進行數(shù)據檢索:

mysql> select * from test6 where gender=2;
+----+-------------+--------+
| id | name | gender |
+----+-------------+--------+
| 2 | gaoxiaofang | f |
+----+-------------+--------+
1 row in set

特別建議,不要使用ENUM存儲數(shù)值,因為無論是排序還是檢索或其他操作,都是根據index值作為條件的,這很容易產生誤解。例如,下面是用ENUM存儲兩個數(shù)值,然后進行檢索和排序操作。

mysql> create table test7(id enum('3','1','2'));
mysql> insert into test7 values('1'),('2'),('3');
# 檢索時id=2,但結果查出來卻為1,因為id=2的2是enum的index值,在enum中index=2的值為1
mysql> select * from test7 where id=2;
+----+
| id |
+----+
| 1 |
+----+
1 row in set
# 按照id進行排序時,也是通過index大小進行排序的
mysql> select * from test7 order by id asc;
+----+
| id |
+----+
| 3 |
| 1 |
| 2 |
+----+
3 rows in set

因此,強烈建議不要在ENUM中存放數(shù)值,即使是浮點型數(shù)值也很容易出現(xiàn)歧義。

1.2.5 SET數(shù)據類型

對于SET類型,和enum類似,不區(qū)分大小寫,存儲時刪除尾隨空格,null也是有效值。但不同的是可以組合多個給出的值。如set('a','b','c','d')可以存儲'a,b','d,b'等,多個成員之間使用逗號隔開。所以,使用多個成員的時候,成員本身的值中不能出現(xiàn)逗號。如果要存儲的內容不在set列表中,則截斷為空值。

SET數(shù)據類型占用的空間大小和SET成員數(shù)量M有關,計算方式為(M+7)/8取整。所以: 1-8個成員占用1個字節(jié);

9-16個成員占用2個字節(jié);
17-24個成員占用3字節(jié);
25-32個成員占用4個字節(jié);
33-64個成員占用8字節(jié)。

MySQL/MariaDB限制最多只能有64個成員。

存儲SET數(shù)據類型的數(shù)據時忽略重復成員并按照枚舉時的順序存儲。如set('b','b','a'),存儲'a,b,a','b,a,b'的結果都是'b,a'。

mysql> create table test8(a set('d','b','a'));
mysql> insert into test8 values('b,b,a'),('b,a,b'),('bab');
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 1
mysql> select * from test8;
+-----+
| a |
+-----+
| b,a |
| b,a |
| |
+-----+
3 rows in set

使用find_in_set(set_value,set_column_name)可以檢索出包含指定set值set_value的行。例如檢索a字段中包含成員b的行:

mysql> select * from test8 where find_in_set('b',a);
+-----+
| a |
+-----+
| b,a |
| b,a |
+-----+
2 rows in set

1.3 數(shù)據類型屬性:unsigned

unsigned屬性就是讓數(shù)值類型的數(shù)據變得無符號化。使用unsigned屬性將會改變數(shù)值數(shù)據類型的范圍,例如tinyint類型帶符號的范圍是-128到127,而使用unsigned時范圍將變成0到255。同時unsigned也會限制該列不能插入負數(shù)值。

create table t(a int unsigned,b int unsigned);
insert into t select 1,2;
insert into t select -1,-2;

上面的語句中,在執(zhí)行第二條語句準備插入負數(shù)時將會報錯,提示超出范圍。

使用unsigned在某些情況下確有其作用,例如一般的ID主鍵列不會允許使用負數(shù),它相當于實現(xiàn)了一個check約束。但是使用unsigned有時候也會出現(xiàn)些不可預料的問題:在進行數(shù)值運算時如果得到負數(shù)將會報錯。例如上面的表t中,字段a和b都是無符號的列,且有一行a=1,b=2。

mysql> select * from t;
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+
1 row in set

此時如果計算a-b將會出錯,不僅如此,只要是unsigned列參與計算并將得到負數(shù)都會出錯。

mysql> select a-b from t;
1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - `test`.`t`.`b`)'
mysql> select a-2 from t;
1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - 2)'

如果計算結果不是負數(shù)時將沒有影響。

mysql> select 2-a,a*3 from t;
+-----+-----+
| 2-a | a*3 |
+-----+-----+
| 1 | 3 |
+-----+-----+
1 row in set

這并不是MySQL/MariaDB中的bug,在C語言中的unsigned也一樣有類似的問題。這個問題在MySQL/MariaDB中設置set sql_mode='no_unsigned_subtraction'即可解決。

所以個人建議不要使用unsigned屬性修飾字段。

1.4 數(shù)據類型屬性:zerofill

zerofill修飾字段后,不足字段顯示部分將使用0來代替空格填充,啟用zerofill后將自動設置unsigned。zerofill一般只在設置了列的顯示寬度后一起使用。關于列的顯示寬度在上文已經介紹過了。

mysql> create table t1(id int(4) zerofill);
mysql> select * from t1;
+-------+
| id |
+-------+
| 0001 |
| 0002 |
| 0011 |
| 83838 |
+-------+
4 rows in set (0.00 sec)

zerofill只是修飾顯示結果,不會影響存儲的數(shù)據值。

以上是“MySQL數(shù)據類型和存儲機制的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI