MySQL 中的數字類型


MySQL 中數據類型常用的就三大類:

  • 數字類型/numeric types
  • 日期和時間/date and time types
  • 字符類型/string (character and byte) types

另外還包含兩個沒那么常用的大類:

  • 特殊類型/spatial types
  • JSON

繼續之前,先來看一些單位上的約定和概念,

  • M:根據具體不同的類型,其表示的意思不一樣,見下方關於這個參數的討論。
  • D 用於定點及浮點數,表示小數點后有多少位。最大可能取值為 30,但不應該超過 M-2。
  • fsp 適用於 TIME, DATETIMETIMESTAMP。可理解秒后面的小數點位數。它應該是介於 0~6 之間的,0 表示沒有小數部分(fractin part)。默認為 0。
  • [] 方括號表示類型中可選的部分。

存儲字符串時指定的類型 VARCHAR(50) 中可接收一個數字作為長度,其實除了字符串類型,數字類型也是可指定該參數的,比如 INT(10)BIGINT(20)。假設后續討論中這個參數使用字母 M 來表示,即上面提到的。該參數被用在不同類型上時,其表示的意思不一樣。

  • 對於整形,它表示 展示寬度/display width
  • 對於定點數(fixed point)或浮點數(floating point),表示能夠存儲的總位數,即精度。
  • 對於字符串,表示能夠存儲的字符串長度。

展示寬度/Display Width

那么什么是展示寬度。展示寬度這個參數具有迷惑性,它不像 CHAR(M) 中有實際意義表示能夠存儲的字符串長度,在數字類型中,它指數字展示時需要的寬度,是 MySQL 格式化時使用的。即 INT(5)INT(15)INT(25) 能夠存儲的數字范圍都是 INT 類型的范圍 -2147483648 ~ 2147483647。如果指定了 ZEROFILL,MySQL 在返回該數字時,對於實際位數小於展示寬度的數字,將自動在左邊補零。比如列的類型為 INT(5),實際存儲了數字 5,返回時會得到 00005。對於沒有指定 ZEROFILL 或實際存儲的位數大於指定的展示寬度,則不會自動補零,此時看上去沒有任何效果。

CREATE TABLE test_zero_fill 
  ( 
     with_fill    INT(5) UNSIGNED ZEROFILL NOT NULL PRIMARY KEY, 
     without_fill INT(5) UNSIGNED NOT NULL 
  ); 
mysql> INSERT INTO test_zero_fill (with_fill, without_fill) VALUES (5, 5),(123456, 123456);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from test_zero_fill;
+-----------+--------------+
| with_fill | without_fill |
+-----------+--------------+
| 00005 | 5 |
| 123456 | 123456 |
+-----------+--------------+
2 rows in set (0.00 sec)

另外,如果使用了 ZEROFILL,該列將自動設置為 UNSIGNED 類型。

mysql> ALTER TABLE test_zero_fill ADD signed_num INT(5) signed ZEROFILL NOT NULL after without_fill;
mysql> describe test_zero_fill;
+--------------+--------------------------+------+-----+---------+-------+
| Field        | Type                     | Null | Key | Default | Extra |
+--------------+--------------------------+------+-----+---------+-------+
| with_fill    | int(5) unsigned zerofill | NO   | PRI | NULL    |       |
| without_fill | int(5) unsigned          | NO   |     | NULL    |       |
| signed_num   | int(5) unsigned zerofill | NO   |     | NULL    |       |
+--------------+--------------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

所以對於數據存儲層面來說,展示寬度其實沒什么用途。如果真的需要格式化,程序中能夠請求 MySQL 的 meta 信息以獲取到相應的展示寬度。

假如在 Node.js 中使用 mysqljs/mysql 作為數據庫連接的模塊,在執行請求時,其回調中返回的 fields 入參便包含了列相應的 meta 信息。

connection.query("SELECT * from test_zero_fill", function(
    error,
    results,
    fields
) {
    if (error) throw error;
    console.log(fields);
});
fields 中包含列的 meta 信息
  FieldPacket {
    catalog: 'def',
    db: 'data_type',
    table: 'test_zero_fill',
    orgTable: 'test_zero_fill',
    name: 'with_fill',
    orgName: 'with_fill',
    charsetNr: 63,
    length: 5,
    type: 3,
    flags: 20579,
    decimals: 0,
    default: undefined,
    zeroFill: true,
    protocol41: true
  },
  FieldPacket {
    catalog: 'def',
    db: 'data_type',
    table: 'test_zero_fill',
    orgTable: 'test_zero_fill',
    name: 'without_fill',
    orgName: 'without_fill',
    charsetNr: 63,
    length: 5,
    type: 3,
    flags: 4129,
    decimals: 0,
    default: undefined,
    zeroFill: false,
    protocol41: true
  },
  FieldPacket {
    catalog: 'def',
    db: 'data_type',
    table: 'test_zero_fill',
    orgTable: 'test_zero_fill',
    name: 'signed_num',
    orgName: 'signed_num',
    charsetNr: 63,
    length: 5,
    type: 3,
    flags: 4193,
    decimals: 0,
    default: undefined,
    zeroFill: true,
    protocol41: true
  }
]

因此,在設計表時,應該關注使用哪種具體的數據類型能夠滿足數據存儲的需要,而不要被展示寬度所迷惑。

數字類型

數字類型分為有符號 SIGNED 和無符號 UNSIGNED 的情況,有符號即最前面有一位符呈位,可表示正負數。默認情況下為 SIGNED 即有符號。

整型

MySQL 中支持標准的 SQL 整型,

  • INTEGER (INT)
  • SMALLINT

並且擴展了一些類型:

  • TINYINT
  • MEDIUMINT
  • BIGINT

以下是 MySQL 中支持的整型,及其對應所需存儲空間和取值范圍。

類型 空間 (字節) 有符號時最小取值 無符號時最小取值 有符號時最大取值 無符號時最大取值
TINYINT 1 -128 0 127 255
SMALLINT 2 -32768 0 32767 65535
MEDIUMINT 3 -8388608 0 8388607 16777215
INT 4 -2147483648 0 2147483647 4294967295
BIGINT 8 -263 0 263-1 264-1

具體到每種類型:

  • TINYINT[(M)] [UNSIGNED] [ZEROFILL]:微整型,取值范圍 -128 ~ 127,無符號情況下為 0 ~ 255。
  • BOOL, BOOLEAN:效果等同 TINYINT(1),0 表示 FALSE,其他非 0 值處理成 TRUE。其中關鍵字 TRUEFALSE 真實代表的是數字 1 和 0。
mysql> SELECT IF(0, 'true', 'false');
+------------------------+
| IF(0, 'true', 'false') |
+------------------------+
| false                  |
+------------------------+

mysql> SELECT IF(1, 'true', 'false');
+------------------------+
| IF(1, 'true', 'false') |
+------------------------+
| true |
+------------------------+

mysql> SELECT IF(2, 'true', 'false');
+------------------------+
| IF(2, 'true', 'false') |
+------------------------+
| true |
+------------------------+

mysql> SELECT IF(0 = FALSE, 'true', 'false');
+--------------------------------+
| IF(0 = FALSE, 'true', 'false') |
+--------------------------------+
| true                           |
+--------------------------------+

mysql> SELECT IF(1 = TRUE, 'true', 'false');
+-------------------------------+
| IF(1 = TRUE, 'true', 'false') |
+-------------------------------+
| true |
+-------------------------------+

mysql> SELECT IF(2 = TRUE, 'true', 'false');
+-------------------------------+
| IF(2 = TRUE, 'true', 'false') |
+-------------------------------+
| false |
+-------------------------------+

mysql> SELECT IF(2 = FALSE, 'true', 'false');
+--------------------------------+
| IF(2 = FALSE, 'true', 'false') |
+--------------------------------+
| false |
+--------------------------------+

關於大整型,關鍵字 SERIAL 等同於 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE

還記得創建表時一般需要指定一個自增的整形 ID 字段么,

CREATE TABLE table_name (id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT)

SERIAL 關鍵字其實是 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE 的別名,所以下次創建表時可直接使用該關鍵字,會省事很多。

CREATE TABLE table_name (id SERIAL PRIMARY KEY)

如果你不想要 BIGINT,SERIAL DEFAULT VALUENOT NULL AUTO_INCREMENT UNIQUE 的別名,那么可以這樣來簡寫 ID 字段:

CREATE TABLE table_name (id INT SERIAL DEFAULT VALUE PRIMARY KEY)

定點型

DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL] 定點型數字,其中 M 表示總的位數(不包含正負號及小數點),D 表示小數位數。D 為 0 則表示沒有小數部分。M 最大取值 65,默認 10;D 最大支持到 30,默認 0。所有的算術運算(+-*/)都基於 65 位的 DECIMAL。

DEC[(M[,D])] [UNSIGNED] [ZEROFILL], NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL], FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]DECIMAL

定點型數字存儲精確的數字,用於准確性要求高的場合,比如涉及金錢。底層實現上,MySQL 使用二進制形式存儲該類型的值。

通常的用法如下:

salary DECIMAL(5,2)

上面示例中,salary 為一個 5 位精度兩位小數的定點型。取值范圍 -999.99 ~ 999.99。

因為 D 缺省時默認為 0,所以 DECIMAL(M) 表示 DECIMAL(M,0),現時,MySQL 中,M 缺省時默認為 10,所以 DECIMAL 表示 DECIMAL(10,0)

當實際存儲的值其小數大於指定的位數時,其精度會自動轉換成所存儲的值的精度。

浮點型

區別於 DECIMAL,浮點型存儲的數字是個近似值。內部存儲時,MySQL 為單精度使用 4 字節(bytes),雙精度使用 8 字節。

浮點型包含以下這些類型:

  • FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]:小型的單精度浮點型。根據 IEEE 標准理論取值范圍 -3.402823466E+38 ~ -1.175494351E-38, 0, 1.175494351E-38 ~ 3.402823466E+38,實際的取值范圍因硬件和操作系統而異,會比理論值要小。
    • M 表示總位數,D 表示小數位數。兩者省略的情況下,其值為硬件允許的最大值。比如 FLOAT(7,4) 看起來會是這個樣子: -999.9999
    • FLOAT[(M,D) 這種形式的類型不是標准的 SQL 類型,后續會廢棄掉。
  • FLOAT(p) [UNSIGNED] [ZEROFILL]:是標准的 SQL 類型,p 表示精度。但 MySQL 中,根據 p 取值的不同,底層實際將其處理成別的類型。比如 0 ~ 24 時,當成 4 字節單精度 FLOAT 類型來處理,25 ~ 53 時處理成 8 字節雙精度的 DOUBLE 類型。
  • DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]:雙精度浮點型。取值范圍 -1.7976931348623157E+308 ~ -2.2250738585072014E-308, 0, 2.2250738585072014E-308 ~ 1.7976931348623157E+308。同 FLOAT(M,D)DOUBLE(M,D) 這種形式的雙精度類型也是非標准 SQL 類型,后續會廢棄。
  • DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL], REAL[(M,D)] [UNSIGNED] [ZEROFILL]:DOUBLE 的別名。

所以實際使用時,為了最大限度的兼容性,直接使用 FLOATDOUBLEPRECISION 而不要指定精度及小數。

BIT 類型

BIT[(M)] 類型用於存儲單個狀態值,M 表示包含幾位。默認為1,最大可取 64。

該類型的值可通過 b'value' 的形式書寫,其中 value 部分以二進制的形式呈現,比如 b'111' 和 b'10000000' 分別表示 7 和 128。更加詳細的信息可參考 9.1.5 Bit-Value Literals

如果賦值到該類型上的值小於 M 指定的位數,值左邊會補零,比如將 b'101' 存儲到類型為 BIT(6) 的列,實際會是 b'000101'。

存儲的值溢出的情況

將要存儲的值超出數字類型的范圍時,其表現跟當前設置的 SQL 模式有關。具體來說,

  • 開啟 SQL 嚴格模式時,超出范圍的值會寫入失敗,MySQL 會中斷操作並且直接拋錯。
  • 非嚴格模式下,MySQL 會將值裁剪到合適的大小進行寫入。即超出的情況下存成該類型能夠接收的最大值。

考察一個通過如下語句創建的表 t1

CREATE TABLE t1 (i1 TINYINT, i2 TINYINT UNSIGNED);

SQL 嚴格模式下,嘗試寫入一個超出范圍的值時拋錯:

mysql> SET sql_mode = 'TRADITIONAL';
mysql> INSERT INTO t1 (i1, i2) VALUES(256, 256);
ERROR 1264 (22003): Out of range value for column 'i1' at row 1
mysql> SELECT * FROM t1;
Empty set (0.00 sec)

以下是非嚴格模式下進行裁剪存儲的情況:

mysql> SET sql_mode = '';
mysql> INSERT INTO t1 (i1, i2) VALUES(256, 256);
mysql> SHOW WARNINGS;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1264 | Out of range value for column 'i1' at row 1 |
| Warning | 1264 | Out of range value for column 'i2' at row 1 |
+---------+------+---------------------------------------------+
mysql> SELECT * FROM t1;
+------+------+
| i1   | i2   |
+------+------+
|  127 |  255 |
+------+------+

上述表現同樣會出現在涉及到對列進行轉換修改的一些操作上,比如 ALTER TABLELOAD DATAUPDATE 以及使用 INSERT 同時插入多行數據時。嚴格模式下會拋錯失敗,非嚴格模式下值會進行裁剪。但失敗的情況不盡相同,如果是事務類型的表,會整個全失敗,其他情況根據具體的值會部分成功,部分失敗。

進行數字計算時如果有溢出,也會拋錯,比如對於 BIGINT 其最大值為 9223372036854775807,因為 MySQL 中默認對數字類型是有符號類型,如下操作會拋錯,

mysql> SELECT 9223372036854775807 + 1;
ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'

對於上述情況,可顯式將 被操作數進行類型轉換,轉成無符號的 BIGINT:

mysql> SELECT CAST(9223372036854775807 AS UNSIGNED) + 1;
+-------------------------------------------+
| CAST(9223372036854775807 AS UNSIGNED) + 1 |
+-------------------------------------------+
|                       9223372036854775808 |
+-------------------------------------------+

通過帶上小數后,轉成 DECIMAL 也能修正上面的錯誤,因為 DECIMAL 比整形要大,

mysql> SELECT 9223372036854775807.0 + 1;
+---------------------------+
| 9223372036854775807.0 + 1 |
+---------------------------+
|     9223372036854775808.0 |
+---------------------------+

兩數相減時,其中一個為無符號數,得出的結果默認為也為無符號。所以如果想減之后結果是負數,則會拋錯。

mysql> SET sql_mode = '';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT CAST(0 AS UNSIGNED) - 1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0 as unsigned) - 1)'

除非開啟了 NO_UNSIGNED_SUBTRACTION

mysql> SET sql_mode = 'NO_UNSIGNED_SUBTRACTION';
mysql> SELECT CAST(0 AS UNSIGNED) - 1;
+-------------------------+
| CAST(0 AS UNSIGNED) - 1 |
+-------------------------+
|                      -1 |
+-------------------------+

總結

對於整型或浮點型,可指定 AUTO_INCREMTN 屬性。指定該屬性性,將不能接收負值。同時 CHECK 屬性與該屬性沖突,也不能同時使用。但對於 FLOAT 和 DOUBLE,AUTO_INCREMENT 屬性的支持將逐漸廢棄掉,實際使用時盡量避免。

對於需要精確數值的場合,使用 DECIMAL,比如涉及金錢的情況。

對於整形,展示寬度不是其存儲的值范圍,只用來格式化。

相關資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM