(MariaDB)MySQL數據類型詳解和存儲機制

来源:http://www.cnblogs.com/f-ck-need-u/archive/2017/10/25/7729251.html
-Advertisement-
Play Games

本文目錄:1.1 數據類型概覽1.2 存儲機制和操作方式 1.2.1 整型的存儲方式 1.2.2 字元類型的存儲方式 1.2.3 日期時間型的存儲方式 1.2.4 ENUM數據類型 1.2.5 SET數據類型1.3. 數據類型屬性:unsigned1.3. 數據類型屬性:zerofill 1.1 數 ...


本文目錄:
1.1 數據類型概覽
1.2 存儲機制和操作方式
 1.2.1 整型的存儲方式
 1.2.2 字元類型的存儲方式
 1.2.3 日期時間型的存儲方式
 1.2.4 ENUM數據類型
 1.2.5 SET數據類型
1.3. 數據類型屬性:unsigned
1.3. 數據類型屬性:zerofill

1.1 數據類型概覽

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

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

各數據類型占用位元組數,參見mariadb官方手冊

1.2 存儲機制和操作方式

數據類型之所以能限定欄位的數據存儲長度,是因為在創建表時在記憶體中嚴格劃定了地址空間,地址空間的長度是多少就能存儲多少位元組的數據。當然,這是一個很粗獷的概念,更具體的存儲方式見下麵的描述。

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

1.2.1 整型的存儲方式

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

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

需要註意,在MySQL/mariadb中的整型數據類型可以使用參數M,M是一個正整數,例如INT(M),tinyint(M)。這個M表示的是顯示長度,如int(4)表示在輸出時將顯示4位整數,如果實際值的位數小於顯示值寬度,則預設使用空格填充在左邊。而結果位數超出時將不影響顯示結果。一般該功能都會配合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的存儲方式以及區別。

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

由於char類型有"短了就使用空格補足"的能力,因此為了體現數據的真實性,在從地址空間中檢索數據時將自動刪除尾隨的空格部分。這正是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常被稱為"變長字元串類型",它存儲數據時使用額外的位元組的bit位來標記某個位元組是否存儲了數據。每存儲一個位元組(不是字元)占用一個bit位進行記錄,因此一個額外的位元組可以標記共256個位元組,2個額外的位元組可以標記65536個位元組。但MySQL/mariadb限制了最大能存儲65536個位元組。這表示,如果是單位元組的字元,它最多能存儲65536個字元,如果是多位元組字元,如UTF8的每個字元占用3個位元組,它最多能存儲65536/3=21845個utf8字元。

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

由於varchar存儲時需要採用額外的bit位記錄每一個位元組,短了的數據不會自動使用補齊,因此顯式存儲的尾隨空格也會被存儲併在Bit位上進行標記,也就是說不會刪除尾隨空格。

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

關於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

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

1.2.3 日期時間型的存儲方式

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

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

1.2.4 ENUM數據類型

ENUM數據類型是枚舉型。定義方式為ENUM('value1','value2','value3',...),在向該類型的欄位中插入數據時只能插入value中的某一個或NULL,插入其他值或空(即'')時都將截斷為空數據。存儲時會忽略大小寫(將轉換為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類型的數據存儲時是通過index數值進行存儲的,相比於字元串類型,它只需要1或2個位元組進行存儲即可理論上,當value的數量少於256個時只需一個位元組,超出256個但少於65536個時使用2個位元組存儲。MySQL/MariaDB限制最多只能存儲65536個value。當然,這是理論上的限制,實際存儲時要考慮的因素有很多,例如NULL也會占用bit位,所以實際存儲時可能250個value就需要2個位元組。

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

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

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

使用ENUM的index進行數據檢索:

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

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

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中存放數值,即使是浮點型數值也很容易出現歧義。

1.2.5 SET數據類型

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

SET數據類型占用的空間大小和SET成員數量M有關,計算方式為(M+7)/8取整。所以: 1-8個成員占用1個位元組;
9-16個成員占用2個位元組;
17-24個成員占用3位元組;
25-32個成員占用4個位元組;
33-64個成員占用8位元組。

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

存儲SET數據類型的數據時忽略重覆成員並按照枚舉時的順序存儲。如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 數據類型屬性:unsigned

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

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

上面的語句中,在執行第二條語句準備插入負數時將會報錯,提示超出範圍。

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

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

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

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)'

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

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 數據類型屬性: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只是修飾顯示結果,不會影響存儲的數據值。

回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html

回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html

轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7729251.html

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近用ionic3+angular4做項目。我遇到了個問題,我返回原來頁面時一般都會調用this.navCtrl.pop()方法,但這個方法不能攜帶參數。怎麼辦? 可以寫個回調方法。 我在a頁面定義個回調方法。 在跳轉到的頁面,接受這個回調方法。 這樣,你就可以返回頁面時也把參數傳回去。 ...
  • ViewPager是v4支持庫中的一個控制項,相信幾乎所有接觸Android開發的人都對它不陌生。之所以還要在這裡翻舊賬,是因為我在最近的項目中有多個需求用到了它,覺得自己對它的認識不夠深刻。我計劃從最簡單的使用場景出發,記錄我到目前為止所對ViewPager的使用情況以及有關它的一些知識點。 這個系 ...
  • --死鎖/************************************************************************************************************************************************* ...
  • 上一篇,我們介紹Impala的相關操作。    下麵我們開始繼續進一步的瞭解Impala的相關操作。 ...
  • 相信使用過Oracle資料庫的人一定碰到過“ORA-12154: TNS: 無法解析指定的連接標識符”錯誤,我在此做一個小小的總結。 在程式中連接Oracle資料庫的方式與其他常用資料庫,如:MySql,Sql Server不同,這些資料庫可以通過直接指定IP的方式連接,但是Oracle則需要通過O ...
  • 在開發或者測試環境的資料庫中,經常會發現有開發或者測試人員誤刪除表或者數據的情況,對於開發或者測試庫,一般都沒有安排定時的備份任務去備份資料庫,一方面是由於存儲資源有限,不太可能給開發或者測試環境準備大量的存儲空間,二是必要性不是很強,開發或者測試庫的資料庫對象變化太多,通過還原備份的方式又有可能沖 ...
  • MySql技術內幕之MySQL入門(1) "MySql技術內幕之MySQL入門(1)" 檢查系統中是否已經安裝了MySQL 若沒有顯示已安裝結果,則沒有安裝。否則表示已經安裝。 安裝過程中會讓輸入密碼,記得把密碼記住。 登錄 本節SQL語句 MySql技術內幕之MySQL入門(2) "MySql技術 ...
  • SQL Server時間類型datetime 相容ADO的COleDateTime. SQL datetime 日期和時間數據,可表示1753.1.1 至 9999.12.31的時間,精度為1/300秒, 約0.33秒, 其在資料庫系統中的存儲為兩個4位元組的整形。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...