標籤:MySQL入門
列的型別
我們前邊說過,MySQL
底層其實把資料儲存到了表裡邊,而表又是由行和列組成的,還是拿我們之前說過的學生基本資訊表
做個例子:
學號 | 姓名 | 性別 | 身份證號 | 學院 | 專業 | 入學時間 |
---|---|---|---|---|---|---|
20180101 | 杜子騰 | 男 | 158177199901044792 | 計算機學院 | 電腦科學與工程 | 2018-09-01 |
20180102 | 杜琦燕 | 女 | 151008199801178529 | 計算機學院 | 電腦科學與工程 | 2018-09-01 |
20180103 | 範統 | 男 | 17156319980116959X | 計算機學院 | 軟體工程 | 2018-09-01 |
20180104 | 史珍香 | 女 | 141992199701078600 | 計算機學院 | 軟體工程 | 2018-09-01 |
20180105 | 範劍 | 男 | 181048200008156368 | 航天學院 | 飛行器設計 | 2018-09-01 |
20180106 | 朱逸群 | 男 | 197995199801078445 | 航海學院 | 電子資訊 | 2018-09-01 |
表裡的一行就代表一個學生的基本資訊,這一行中的某一列就代表這個學生基本資訊中的一項屬性,也就是說學號
是學生的一項屬性、姓名
也是學生的一項屬性,其他的列也都是這個學生的屬性。但是這些屬性都有一定格式,比如說學號必須是整數格式的,入學時間必須是日期格式的,其他的屬性都是字串格式的,不同格式的資料是不能隨便亂填的,你把一個日期格式的資料填在了性別裡,豈不是鬧出了笑話。所以設計MySQL
的大叔們針對每一種格式定義了一種相應的型別,我們接下來就要詳細嘮叨具體有哪些型別以及它們是怎麼儲存在計算機中的。
注:身份證號由於最後以為可能是X,所以就歸為字串了。
數值型別
整數型別
進位制
long long ago,原始人是沒有現在的人這麼聰明的,只會用十分簡單的東西計數。比方說他們只會使用麥稈來統計獵物數量,每收穫一個獵物就在麥稈堆裡新增一支麥稈,所以如果獵物多的話,麥稈就會累積很多,數都數不過來。後來人們發現不用這麼笨,可以把麥稈摺疊成不同的形狀來代表不同的數量。普通的麥稈就代表1,也就是說每新增一隻獵物就多放一隻普通麥稈;如果當前已經放了9根普通的麥稈,此時再新增了一隻獵物,就用一個心形麥稈來取代之前的9根普通麥稈;假如當前已經有了9根心形麥稈和9根普通麥稈時又新增了一隻獵物的話,就用一根矩形麥稈來替代之前所有的麥稈。像這種每逢10個數向前進一位的計數方法就叫做十進位制,這樣他們就可以用很少的麥稈來表示很大的數字了。
如果在計數的時候每逢8個數就往前進一位就是八進位制,每逢9個數就往前進一位就是九進位制。生活中常用的進位制除了十進位制外,還有鐘錶裡用來統計時間的十二進位制和六十進位制,用在計算機裡的二進位制和十六進位制。
以十進位制數字109
為例,它表示有1個10²
,加0個10¹
,加9個10⁰
,用數學符號表示就是:
109 = 1 × 10² + 0 × 10¹ + 9 × 10⁰
複製程式碼
這個數字也可以這麼表示:
109 = 1 × 8² + 5 × 8¹ + 5 × 8⁰
複製程式碼
也就是從八進位制的逢8進一的角度上考慮,這個數可以被表示為155
(八進位制)。當然這個數也可以這麼寫:
209 = 1 × 2⁶ + 1 × 2⁵ + 0 × 2⁴ + 1 × 2³ + 1 × 2² + 0 × 2¹ + 1 × 2⁰
複製程式碼
也就是從二進位制的逢2進一的角度上考慮,這個數也可以被表示為:1101101
(二進位制)。又因為計算機中一般用8個位表示一個位元組,平時都是用若干個位元組來表示一個整數,假如用1個位元組表示109
的話,那效果就是這樣:01101101
,假如用兩個位元組表示十進位制數19
的話,那效果就是這樣:0000000001101101
。
MySQL的整數型別
很顯然,使用的位元組數越多,意味著能表示的數值範圍就越大,但是也就越耗費儲存空間。根據表示一個數佔用位元組數的不同,MySQL
把整數劃分成如下所示的型別:
型別 | 位元組數 | 無符號數取值範圍 | 有符號數取值範圍 | 含義 |
---|---|---|---|---|
TINYINT |
1 | 0 ~ 2⁸-1 | -2⁷ ~ 2⁷-1 | 非常小的整數 |
SMALLINT |
2 | 0 ~ 2¹⁶-1 | -2¹⁶ ~ 2¹⁶-1 | 小的整數 |
MEDIUMINT |
3 | 0 ~ 2²⁴-1 | -2²⁴ ~ 2²⁴-1 | 中等大小的整數 |
INT |
4 | 0 ~ 2³²-1 | -2³² ~ 2³²-1 | 標準的整數 |
BIGINT |
8 | 0 ~ 2⁶⁴-1 | -2⁶⁴ ~ 2⁶⁴-1 | 大整數 |
以TINYINT
為例,用1個位元組,也就是8個位表示有符號數
的話,就是既可以表示正數,也可以表示負數的話,需要有一個二進位制位表示正負號。但是如果表示無符號數
的話,也就是隻表示非負數的話,就不需要表示正負號,這是有符號數
和無符號數
的區別。具體每個型別的取值範圍是如何計算出來的我們這就不嘮叨了,可以找一本計算機基礎的書看看。
浮點數型別
用二進位制表示十進位制小數
浮點數就是小數,我們平時用的的十進位制小數也可以被轉換成二進位制後被計算機儲存。比如9.875
,這個小數可以被表示成這樣:
9.875 = 8 + 1 + 0.5 + 0.25 + 0.125 = 1 × 2³ + 1 × 2⁰ + 1 × 2⁻¹ + 1 × 2⁻² + 1 × 2⁻³
複製程式碼
也就是說,如果十進位制小數9.875
轉換成二進位制小數的話就是:1001.111
。為了在計算機裡儲存這種二進位制小數,我們統一把它們表示成a × 2ⁿ
的科學計數法的形式,其中1≤|a|
<2,比如1001.111
可以被表示成1.001111 × 2³
,我們把小數點之後的001111
稱為尾數
,把2³
中的3
稱為指數
,然後只需要在計算機中的二進位制位中表示出尾數
和指數
就行了。用科學技術法來計數的時候,小數的小數點看起來就像移動了一樣,所以這樣的小數也叫做浮點數
。
當然,如果你需要表示有符號小數
,那還得用一個二進位制位來表示正負號。如果我們用四個位元組,也就是32個位來表示一個有符號小數的話,我們可以這麼劃分各個部分所表示的意義:
MySQL的浮點數型別
很顯然,使用的位元組數越多,表示尾數
和指數
的範圍就越大,也就是說表示的小數範圍就越大,所以設計MySQL
的大叔根據表示一個小數需要的不同位元組數定義瞭如下的兩種型別來表示小數:
型別 | 位元組數 | 絕對值最小非0值 | 絕對值最大非0值 | 含義 |
---|---|---|---|---|
FLOAT |
4 | ±1.175494351e-38 | ±3.402823466e38 | 單精度浮點數 |
DOUBLE |
8 | ±2.2250738585072014e-308 | ±1.7976931348623157e308 | 雙精度浮點數 |
需要注意的是,雖然有的十進位制小數,比如1.875
可以被很容易的轉換成二進位制數0.111
,但是更多的小數是無法直接轉換成二進位制的,比如說0.3
,它轉換成的二進位制小數就是一個無限小數,但是我們現在只能用4個位元組或者8個位元組來表示這個小數,所以只能進行四捨五入來近似的表示,所以我們說計算機的浮點數表示有時是不精確的。
設定有效位數和小數位數
對於我們使用者而言,使用的都是十進位制小數。表示一個小數需要的數字個數稱為有效位數
,小數點後的數字個數稱為小數位數
,11.2
和-0.0021
這兩個小數的有效位數和小數位數見下表:
小數 | 有效位數 | 小數位數 |
---|---|---|
11.2 |
3 | 1 |
-0.0021 |
5 | 4 |
如果我們知道某列屬性需要的有效位數和小數位數,我們可以用這樣的方式手動指定一下:
FLOAT(M, D) 或 DOUBLE(M, D),其中M指`有效位數`,D指`小數位數`
複製程式碼
舉個例子看一下,設定了有效位數和小數位數的單精度浮點數的取值範圍的變化:
型別 | 取值範圍 |
---|---|
FLOAT(4, 1) |
-999.9~999.9 |
FLOAT(5, 1) |
-9999.9~9999.9 |
FLOAT(6, 1) |
-99999.9~99999.9 |
FLOAT(4, 0) |
-9999~9999 |
FLOAT(4, 1) |
-999.9~999.9 |
FLOAT(4, 2) |
-99.99~90.99 |
可以看到,在小數位數相同的情況下,有效位數越多,該型別的取值範圍越大;在有效位數相同的情況下,小數位數越大,該型別的取值範圍越小。當然,M
和D
的取值也不是無限大的,你要是把有效位數取成一個億,那記憶體不得崩了麼,別忘了單精度浮點數只有4個位元組的儲存空間,雙精度浮點數只有8個位元組的儲存空間,超過了這個儲存空間所能表達的小數就無效了。
定點數型別
正因為用浮點數表示小數可能會有不精確的情況,在一些情況下我們必須保證小數是精確的,所以設計MySQL
的大叔們提出了定點數的概念,它也是儲存小數的一種方式:
型別 | 位元組數 | 取值範圍 |
---|---|---|
DECIMAL(M, D) |
取決於M和D | 取決於M和D |
此處的M
指的就是有效位數,D
指的就是小數位數。M
和D
對取值範圍的影響我們之前在嘮叨浮點數的時候已經介紹過了,但是為啥M
和D
的取值還會影響到需要的位元組數呢?
我們說定點數是一種精確的小數,為了達到精確的目的我們不能把它轉換成二進位制之後再儲存(這可能會產生四捨五入的情況)。我們可以以小數點為界,把一個小數看成是兩個整數被小數點分隔開來的樣子,所以我們可以把一個小數分成3塊來儲存,一是小數點左邊的整數,而是小數點本身,三是小數點右邊的整數,這樣就可以保證儲存的小數肯定是精確的了。
以DECIMAL(6, 1)
為例,這種型別的取值範圍是-99999.9~99999.9
。所以整數部分絕對值最大的就是99999
,小數部分最大的就是9
,而整數部分的最大值99999
這個十進位制數最少需要用3個位元組才能存放,小數部分的最大值9
只需要1個位元組就可以存放,所以最後這個定點數型別就需要使用5個位元組,各個部分的劃分如下:
所以不同的有效位數和小數位數,最終需要的位元組數也是不同的。可以看到,與浮點數相比,定點數需要更多的空間來儲存資料,所以如果不是像工資一樣特重要的資料,一般的小數用浮點數就足夠了。
M
和D
都是可選的,預設的M
的值是10,預設的D
的值是0,也就是說下列等式是成立的:
DECIMAL = DECIMAL(10) = DECIMAL(10, 0)
DECIMAL(n) = DECIMAL(n, 0)
複製程式碼
另外,有效位數和小數位數也不是無限大的(太大了記憶體受不了),限制M
的範圍是1~65
,D
的範圍是0~30
,且D
的值不能超過M
。
非負數值型別的表示
對於數值型別,包括整數、浮點數和定點數,有些情況下我們只需要用到非負數,而且對於整數來說,單純的表示非負數能將正整數的表示範圍提升一倍,所以MySQL
給我們提供了一個表示非負數值型別的方式,就是在原數值型別後加一個單詞UNSIGNED
:
數值型別 UNSIGNED
複製程式碼
大家可以把它當成一種新型別對待,比如INT UNSIGNED
就表示非負整數,取值範圍是0 ~ 2³²-1
。
字串型別
字元和字串
字元
分為兩種,一種叫可見字元
,一種叫不可見字元
。顧名思義,可見字元
就是列印出來後能看見的字元。比如a
,b
,我
,。
... 這樣的人眼能看見的單個國家文字、標點符號、圖形符號、數字等這樣的東東,我們就叫做一個可見字元
。不可見字元
也好理解,就是印表機或者在黑框框裡列印字元的時候有時候需要換行,打個製表符啥的,或者在輸出某個字元的時候就發出嘟
地一聲,這種我們看不到,只是為了控制輸出效果的字元叫做不可見字元
。字串
就是把字元連起來的樣子,比如abc
,就是由a
、b
、c
三個字元連起來的一個字串
,下邊列舉了4個字串的例子:
'我喜歡你'
'me, too'
'give me a hug'
'麼麼噠'
複製程式碼
字元編碼簡介
在具體分析MySQL
中各個字串型別之前,我們一定要先搞明白字元和位元組的區別。字元是面向人的概念,位元組是面向計算機的概念。如果你想在計算機中表示字元,那就需要通過字元編碼
來將一個字元對映到一個二進位制資料。不幸的是,這種對映關係並不是唯一的,不同的人制作了不同的字元編碼,根據表示一個字元使用的位元組數是不是相同的,編碼方式可以分為下邊兩種:
-
固定長度的編碼
採用相同的位元組數來表示一個字元,比方說
ASCII
編碼方式採用1個位元組來表示一個字元,ucs2
採用2個位元組來表示一個字元。 -
變長編碼 採用長度不一樣的位元組來表示不同的字元,比方說
utf8
編碼方式採用1~3
個位元組來編碼一個字元,gb2312
採用1~2
個位元組來編碼一個字元。
對於不同的字元編碼方式來說,對同樣一個字元可能會產生不一樣的編碼,比如同樣一個字元:我
,在utf8
和gb2312
這兩種編碼方式下被對映成如下的二進位制資料如下:
`utf8`編碼:111001101000100010010001 (3個位元組,十六進位制表示是:0xE68891)
`gb2312`編碼:1100111011010010 (2個位元組,十六進位制表示是:0xCED2)
注:十六進位制前邊的`0x`是字首,表示後邊的是16進位制資料。
複製程式碼
如果你對上邊說的話一知半解,那你需要系統的學習一下字符集的相關概念:字符集和編碼介紹。
小貼士: MySQL對`utf8`編碼做了限制,它只能使用3個位元組去編碼字元,如果有的字元需要4個位元組的話,可以使用`utfmb4`,這只是出於效能考慮的,畢竟我們的大部分場景中不會使用到4個位元組編碼的字元。
MySQL的字串型別
現在我們可以看一下MySQL
中提供的各種字串型別(注:其中M
代表你要儲存的字串中最多包含的字元數量,L
代表在實際字串在某個字元編碼下所佔用的位元組數,W
代表某個固定長度編碼方式中編碼一個字元需要的位元組數):
型別 | 最大長度 | 儲存空間要求 | 含義 |
---|---|---|---|
CHAR(M) |
M個字元 | L個位元組或M個位元組或M×W個位元組 | 固定長度的字串 |
VARCHAR(M) |
M個字元 | L+1 或 L+2 個位元組 | 可變長度的字串 |
TINYTEXT |
2⁸-1 個位元組 | L+1個位元組 | 非常小型的字串 |
TEXT |
2¹⁶-1 個位元組 | L+2 個位元組 | 小型的字串 |
MEDIUMTEXT |
2²⁴-1 個位元組 | L+3個位元組 | 中等大小的字串 |
LONGTEXT |
2³²-1 個位元組 | L+4個位元組 | 大型的字串 |
CHAR(M)
CHAR(M)
中的M
取值範圍是0~255
。如果省略掉M
的值,那它的預設值就是1,也就是說CHAR
和CHAR(1)
是一個意思。再回頭看一眼我們的學生基本資訊表,如果你覺得學生的姓名不會超過5個字元,你就可以指定這個姓名列的型別為CHAR(5)
。
CHAR(M)
在不同的編碼方式下需要的儲存空間也是不一樣的。假設我們實際儲存的字串在某個編碼方式下佔用的位元組數為L
,則CHAR(M)
實際佔用的位元組數取決於該編碼方式是否是固定長度的以及M
和L
的值,具體計算方式如下:
-
如果該編碼方式是固定長度的,
W
代表固定長度的位元組數,比方說ASCII
編碼方式的W
的值就是1
,ucs2
編碼方式的W
的值就是2
,則CHAR(M)
佔用的儲存空間大小與實際儲存的資料無關,都是M×W
個位元組。 -
如果該編碼方式是變長的,則分為兩種情況:
-
如果
L
小於M
,則佔用的儲存空間大小為M
個位元組。 -
如果
L
大於M
,則佔用的儲存空間大小為L
個位元組。
-
比方說我們現在使用的字串型別為CHAR(5)
,所以現在M
的值就是5
,然後我們看一下CHAR(5)
實際佔用的儲存空間的情況:
-
如果我們使用固定長度編碼
ucs2
進行編碼,不管儲存的資料是啥,最後佔用的儲存空間都一樣。W
的值是2,所以CHAR(5)
佔用的空間就是5×2=10
個位元組。 -
如果我們使用變長的
utf8
進行編碼。-
假設實際儲存的字串為
我
,因為它用utf8
編碼後的結果是0xE68891
,也就是說實際佔用空間L
的值為3
,又因為L < M
,所以實際佔用的儲存空間大小為5
個位元組。 -
假設實際儲存的字串為
我我
,因為它用utf8
編碼後的結果是0xE68891E68891
,也就是說實際佔用空間L
的值為6
,又因為L > M
,所以實際佔用的儲存空間大小為6
個位元組。
-
小貼士: 可以看到,`CHAR(M)`只有在採用固定長度的字元編碼時所佔用的儲存空間大小才是確定的,否則佔用儲存空間大小與實際字串佔用的位元組長度有關。
需要注意的是:如果CHAR(M)
佔用的儲存空間大於實際字串需要的儲存空間,那多出的儲存空間將被空格
填滿。這種CHAR(M)
的字串型別有一個非常不好的地方:一旦你確定了M
的值,如果M
的值很大,而你實際儲存的字串佔用位元組數又很少,會造成儲存空間的浪費。
VARCHAR(M)
如果你表中的某個列需要儲存字串型別的資料,而且這些字串長短不一,那麼使用CHAR(M)
可能造成很大程度上的浪費,VARCHAR(M)
正是為了解決這個問題而生的。
VARCHAR(M)
中的M
也是指你要儲存的字串中最多包含的字元數量,取值範圍是1~65535
。但是MySQL
中還有一個規定,就是某一行包含的所有列中儲存的資料大小不得超過65535個位元組,所以VARCHAR(M)
實際能夠容納的字元數量肯定小於65535。
一個VARCHAR(M)
型別表示的資料由這麼兩部分組成:
-
真正的字串內容。
-
佔用位元組數。
假設真正的字串內容編碼後佔用位元組數為
L
,如果L
不大於255,也就是位元組數可以用1個位元組來表示,那麼實際佔用的儲存空間就是L+1
個位元組;如果L
大於255且不大於65535,也就是位元組數可以用2個位元組來表示,那麼實際佔用的儲存空間就是L+2
個位元組;又因為某一行包含的所有列中儲存的資料大小不得超過65535個位元組,所以L
不可能大於65535。所以實際佔用的儲存空間只可能是L+1
個位元組或者L+2
個位元組。
我們還用學生的姓名屬性做例子,在使用utf8
編碼方式的情況下,杜子騰
和範統
這兩個字串可以被編碼成如下的樣子(二進位制太長了,用16進製表示):
`杜子騰`:0xE69D9CE5AD90E885BE (共9個位元組)
`範統`:0xE88C83E7BB9F (共6個位元組)
複製程式碼
假設我們給姓名列定義的型別為VARCHAR(5)
,我們看一下這兩個字串的實際儲存示意圖:
從這個圖例也可以看出了,VARCHAR(M)
佔用的儲存空間大小隨著實際儲存的內容變化而變化,所以我們說 VARCHAR(M) 是一種可變長度的字串型別。
各種TEXT型別
雖然VARCHAR(M)
已經可以儲存很長的字串了,可是還是不夠咋辦?對於很長的字串,設計MySQL
的大叔們給我們提供了TINYTEXT
、TEXT
、MEDIUMTEXT
、LONGTEXT
四種可以儲存大型的字串的型別。它們也都是變長型別,由實際內容和內容長度構成,因為TINYTEXT
最多可以儲存2⁸-1
個位元組,所以內容長度用1個位元組就可以表示,TEXT
最多可以儲存2¹⁶-1
個位元組,所以內容長度用2個位元組就可以表示,剩下的兩個的內容長度佔用的位元組數也都是按這個規則算出來的。
不是之前有個規定說某一行包含的所有列中儲存的資料大小不得超過65535個位元組麼?那TEXT
、MEDIUMTEXT
、LONGTEXT
這3個型別豈不是不符合規定嘍?哈,由於MySQL
的大叔會特別關照這幾種型別,所以這幾個型別並不在這個規定的限制範圍之內。大家如果有什麼特別長的文字就可以考慮使用這幾個型別了。
ENUM型別和SET型別
視角回到我們的學生資訊表,性別一列也需要填寫字串,但是比較特殊的一點是,這一列只能填男
或者女
,填別的字串就尷尬了!針對這種情況,我們提出了一個叫ENUM
的型別,也稱為列舉型別
,它的格式如下:
ENUM('str1', 'str2', 'str3' ⋯)
複製程式碼
它表示在給定的字串列表裡選擇一個。比如我們的性別一列可以定義成ENUM('男', '女')
型別。這個的意思就是性別一列只能在'男'
或者'女'
這兩個字串之間選擇一個,相當於一個單選框~
有的時候某一列的值可以在給定的字串列表中挑選多個,假設學生的基本資訊加了一列興趣
屬性,這個屬性的值可以從給定的興趣列表中挑選多個,那我們可以使用SET
型別,它的格式如下:
SET('str1', 'str2', 'str3' ⋯)
複製程式碼
它表示可以在給定的字串列表裡選擇多個。我們的興趣一列就可以定義成SET('打球', '畫畫', '扯犢子', '玩遊戲')
型別。這個的意思就是興趣一列可以在給定的這幾個字串中選擇一個或多個,相當於一個多選框~效果就像這樣:
學號 | 姓名 | ··· | 興趣 |
---|---|---|---|
20180101 | 杜子騰 | ··· | '打球', '畫畫' |
20180102 | 杜琦燕 | ··· | '扯犢子' |
20180103 | 範統 | ··· | '扯犢子', '玩遊戲' |
20180104 | 史珍香 | ··· | '畫畫', '扯犢子', '玩遊戲' |
ENUM 和 SET 都是一種特殊的字串型別,在從字串列表中單選或多選元素的時候用得到它們。
日期與時間型別
我們有很多場景需要表示時間或日期,比如學生基本資訊中的入學時間
就需要用日期的格式儲存。MySQL
為我們提供了多種關於時間和日期的型別,各種型別能表示的範圍如下:
型別 | 儲存空間要求 | 取值範圍 | 含義 |
---|---|---|---|
YEAR |
1位元組 | 1901~2155 | 年份值 |
DATE |
3位元組 | '1000-01-01' ~ '9999-12-31' | 日期值 |
TIME |
3位元組 | '-838:59:59' ~ '838:59:59' | 時間值 |
DATETIME |
8位元組 | '1000-01-01 00:00:00' ~ '9999-12-31 23:59:59' | 日期加時間值 |
TIMESTAMP |
4位元組 | '1970-01-01 00:00:01' ~ '2038-01-19 03:14:07' | 時間戳 |
在MySQL5.6.4
這個版本之後,TIME
、DATETIME
、TIMESTAMP
這幾種型別新增了對毫秒、微妙的支援。由於毫秒、微秒都不到1秒,所以也被稱為小數秒
,MySQL
最多支援6位小數秒的精度,各個位代表的意思如下:
我們可以選擇TIME
、DATETIME
、TIMESTAMP
這幾種型別最多支援到小數點後幾位的時間精度,通用格式為:
型別(小數秒位數)
其中的`小數秒位數`可以在0、1、2、3、4、5、6中選擇
複製程式碼
比如DATETIME(0)
表示精確到秒,DATETIME(3)
表示精確到毫秒,DATETIME(5)
表示精確到10微秒。如果你在選擇TIME
、DATETIME
、TIMESTAMP
這幾種型別的時候新增了對小數秒的支援,那麼儲存空間需要相應的擴大,不同的小數秒精度需要的儲存空間不同,如下表:
小數秒精度 | 儲存空間要求 |
---|---|
0 | 0位元組 |
1或2 | 1位元組 |
3或4 | 2位元組 |
5或6 | 3位元組 |
也就是說如果你選擇使用DATETIME(1)
,那麼需要的儲存空間就是在DATETIME
的空間上再加上小數秒需要的空間,就是8+1=9
個位元組,類似的,DATETIME(3)
就需要10個位元組。所以,MySQL5.6.4
這個版本之後的各個型別需要的儲存空間和取值範圍就如下:
型別 | 儲存空間要求 | 取值範圍 | 含義 |
---|---|---|---|
YEAR |
1位元組 | 1901~2155 | 年份值 |
DATE |
3位元組 | '1000-01-01' ~ '9999-12-31' | 日期值 |
TIME |
3位元組+小數秒的儲存空間 | '-838:59:59[.000000]' ~ '838:59:59[.000000]' | 時間值 |
DATETIME |
8位元組+小數秒的儲存空間 | '1000-01-01 00:00:00[.000000]' ~ '9999-12-31 23:59:59'[.999999] | 日期加時間值 |
TIMESTAMP |
4位元組+小數秒的儲存空間 | '1970-01-01 00:00:01[.000000]' ~ '2038-01-19 03:14:07'[.999999] | 時間戳 |
當然,如果你使用的MySQL
版本還沒到5.6.4
,那就不支援小數秒,可以翻上去看原來的儲存空間和取值範圍。下邊我們來詳細看一下各種型別。
YEAR
單純的表示一個年份值而已~不過它只有1個位元組大小,所以儲存的年份值範圍有限,如果我們想儲存更多的年份值,可以考慮更換成SMALLINT
(2位元組)或者字串型別啥的~
DATE、TIME和DATETIME
顧名思義,DATE
表示日期,格式是CCYY-MM-DD
;TIME
表示時間,格式是hh:mm:ss[.uuuuuu]
,DATETIME
表示日期+時間,格式是CCYY-MM-DD hh:mm:ss[.uuuuuu]
。其中的CC
、YY
、MM
、DD
、hh
、mm
、ss
、uuuuuu
分別表示世紀、年、月、日、時、分、秒、小數秒。
需要注意的是,DATETIME 中的時間部分表示的是一天內的時間,而 TIME 表示的是一段時間,而且可以表示負值。
TIMESTAMP
1970-01-01 00:00:00
註定是一個特殊的時刻,這一天被稱為位零日
,也稱為紀元
,MySQL
中把某個時刻距離1970-01-01 00:00:00
的秒數稱為時間戳
。比方說當前時間是2018-01-24 11:39:21
,距離1970-01-01 00:00:00
的秒數為1516765161
,那麼2018-01-24 11:39:21
這個時刻的時間戳就是1516765161
。不過在MySQL5.6.4
之後,時間戳的值也可以加入小數秒。
用時間戳儲存時間的好處就是,它展示的值可以隨著時區的變化而變化。比方說我們把2018-01-24 11:39:21
這個時刻儲存到一個TIMESTAMP
的列中,那麼在中國你看到的時間就是2018-01-24 11:39:21
,如果你去了日本,他們哪裡的使用的是東京時間,比北京時間早一個小時,所以他們那顯示的就是2018-01-24 10:39:21
。而如果你用DATETIME
儲存2018-01-24 11:39:21
的話,那不同時區看到的時間值都是一樣的。
MySQL中有設定時區的方式,我們這裡先不嘮叨,等用到的時候再說啊
複製程式碼
二進位制型別
如果你是專業的程式設計師的話,肯定對二進位制資料不陌生,有時我們也有儲存這些二進位制資料的需求。MySQL
提供了下邊這些資料型別供我們使用:
型別 | 最大長度 | 儲存空間要求 | 含義 |
---|---|---|---|
BIT(M) |
M個位 | (M+7)/8個位元組 | 固定長度的字串 |
BINARY(M) |
M個位元組 | M個位元組 | 固定長度的字串 |
VARBINARY(M) |
M個位元組 | L+1 或 L+2 個位元組 | 可變長度的字串 |
TINYBLOB |
2⁸-1 個位元組 | L+1個位元組 | 非常小型的字串 |
BLOB |
2¹⁶-1 個位元組 | L+2個位元組 | 小型的字串 |
MEDIUMBLOB |
2²⁴-1 個位元組 | L+3個位元組 | 中等大小的字串 |
LONGBLOB |
2³²-1 個位元組 | L+4個位元組 | 大型的字串 |
BIT(M)
對於BIT(M)
來說,M
指的是該型別最多能儲存的二進位制位的個數,比如BIT(3)
就是指最多能存放3個二進位制位。而記憶體空間最少的分配單位就是位元組,所以不足一位元組的按一位元組計算,所以儲存空間就是(M+7)/8個位元組。
BINARY(M)與VARBINARY(M)
BINARY(M)
和VARBINARY(M)
對應於我們前邊提到的CHAR(M)
和VARCHAR(M)
,都是前者是固定長度的型別,後者是可變長度的型別,只不過BINARY(M)
和VARBINARY(M)
是用來存放位元組的,而CHAR(M)
和VARCHAR(M)
是用來儲存字元的。
其他的二進位制型別
TINYBLOB
、BLOB
、MEDIUMBLOB
、LONGBLOB
是針對資料量很大的二進位制資料提出的,比如圖片、音訊、壓縮檔案啥的。它們很像TINYTEXT
、TEXT
、MEDIUMTEXT
、LONGTEXT
,不過BLOB
是用來儲存位元組的,而TEXT
是用來儲存字元的而已。在通常情況下,我們一般都是隻儲存個檔案路徑而已,然後使用作業系統的檔案系統去訪問檔案的~
總結
資料庫底層使用表來存放資料的,一張表有很多列,每個列都有可能存放不同格式的資料,不同格式的資料是不能混用的,所以MySQL
提出了許多型別來儲存不同格式的資料。
用來儲存整數的型別有TINYINT
、SMALLINT
、MEDIUMINT
、INT
、BIGINT
這幾種,它們需要的儲存空間不同,所以能表示的整數範圍也不同。
用來儲存小數的有FLOAT
、DOUBLE
、DECIMAL
型別,前兩個屬於浮點型,後一個屬於定點型,浮點型更省儲存空間,而定點型更精確,這些表示小數的型別都可以使用(M, D)來指定小數的有效位數和小數位數。
用來儲存字串的有CHAR(M)
、VARCHAR
、TEXT
、MEDIUMTEXT
、LONGTEXT
這幾種,它們實際佔用的位元組儲存空間依賴與我們當前使用的字元編碼,因為在不同編碼下,同一個字元可能被編碼成不同長度的位元組資料,其中的CHAR(M)
是固定長度的型別,其餘集中都是可變長度的型別,真實長度取決於實際的字串長度。
用來儲存時間的有YEAR
、DATE
、TIME
、DATETIME
、TIMESTAMP
這幾種型別,需要注意的是,在在MySQL5.6.4
這個版本之後,TIME
、DATETIME
、TIMESTAMP
這幾種型別新增了對小數秒的支援,但是儲存小數秒又要使用額外的儲存空間。另外,TIMESTAMP
這種型別儲存的是自1970-01-01 00:00:00
時刻起的秒數,所以在不同時區下會顯示不同的時間值。
用來儲存二進位制資料的有BIT(M)
、BINARY(M)
、VARBINARY(M)
、TINYBLOB
、BLOB
、MEDIUMBLOB
、LONGBLOB
這幾種,除了BIT(M)
是以二進位制位為單位的以外,其餘的型別都是以位元組為單位的,並且它們的使用類似字串中的各種型別,只不過一個是以字元為單位,一個以位元組為單位而已~
小冊
本系列專欄都是MySQL入門知識,想看進階知識可以到小冊中檢視:MySQL是怎樣執行的連結 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL核心的一些核心概念,比如記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想降低普通程式設計師學習MySQL核心的難度,讓學習曲線更平滑一點~