MySQL 中的數字類型

原文: MySQL 中的數字類型

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

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

另外還包含兩個沒那麼經常使用的大類:html

  • 特殊類型/spatial types
  • JSON

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

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

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

  • 對於整形,它表示 展現寬度/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 或實際存儲的位數大於指定的展現寬度,則不會自動補零,此時看上去沒有任何效果。github

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 類型。sql

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 信息以獲取到相應的展現寬度。shell

假如在 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
  }
]

所以,在設計表時,應該關注使用哪一種具體的數據類型可以知足數據存儲的須要,而不要被展現寬度所迷惑。ui

數字類型

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

整型

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,好比涉及金錢的狀況。

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

相關資源

相關文章
相關標籤/搜索