第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
慕課專欄

目錄

索引目錄

MySQL 開發(fā)高頻面試題精選

原價 ¥ 48.00

立即訂閱
03 為什么會有程序員使用INT(20)或INT(1)這樣的設計,這樣是否合理?
更新時間:2020-09-22 10:16:38
低頭要有勇氣,抬頭要有底氣。——韓寒

在上一節(jié)中我們了解了 VARCHAR 這個使用頻率很高的數據類型的用法,并介紹了 VARCHAR 如何優(yōu)化的一些知識點,比如要避免行溢出,避免設計了太多的 VARCHAR 字段導致無法擴展的尷尬等等,希望你能從中得到一些收獲。

這一節(jié)我們來學習整形數據類型,那么整形這個數據類型又該如何使用呢?

1. INT 1、INT 20 與顯示寬度

在我的日常工作當中,經常遇到一些程序員朋友發(fā)來一些建庫建表語句,其中的部分字段是這樣子的:

status int(1) not null default '1'
type int(1) not null default '1'

那么 INT (1) 就真的是 1 個字節(jié)了嗎,這個括號中間的數字究竟有什么意義?

我們還是做一個實驗來看一下。

下面我們創(chuàng)建一個數據類型都是 INT 的新表,這個表里面有 3 個字段,分別定義為 INT、INT (1)、和 INT (20),并且插入一些數據:

mysql> create table int_test(id int auto_increment, col_1 int(1), col_2 int(20), primary key (id));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into int_test values(1,1,1),(10,10,10);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

查詢一下結果,看下輸出的值:

mysql> select * from int_test;
+----+-------+-------+
| id | col_1 | col_2 |
+----+-------+-------+
|  1 |     1 |     1 |
| 10 |    10 |    10 |
+----+-------+-------+
2 rows in set (0.01 sec)

在這個實驗中你可以看到,不管你定義 INT (1) 還是 INT (20),MySQL 在存儲的時候都不會做出超出 INT 的限制范圍。

那這個括號里面的值是做什么用的呢?

其實它定義的是顯示寬度。

當你在程序中定義顯示寬度之后,如果你插入的值不夠這個寬度,在查詢時會在左邊用相應寬度的空格填充。

這個空格在通常情況下,我們是看不到的,因為在客戶端輸出時(MySQL Workbench、mysql-connector-java 等客戶端)默認會去掉左側的空格。

所以為什么說它是顯示寬度,是因為它并不限制存儲的值的范圍,不管是 INT (1) 還是 INT (20),存儲范圍都是 INT

  • 有符號:-2147483648 ~ 2147483647
  • 無符號:0 ~ 4294967295

這里再提及一點,整形數據類型中還有一個 ZEROFILL 的屬性,如果建表時指定了這個屬性,剛剛的空格就會變成 0,并且自動給這個列加上 UNSIGNED 屬性。

我們再來做實驗看下:

mysql> show create table int_test \G
*************************** 1. row ***************************
       Table: int_test
Create Table: CREATE TABLE `int_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `col_1` int(1) DEFAULT NULL,
  `col_2` int(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
1 row in set (0.01 sec)

mysql> alter table int_test modify col_2 int(20) zerofill;
Query OK, 2 rows affected (0.07 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> show create table int_test \G
*************************** 1. row ***************************
       Table: int_test
Create Table: CREATE TABLE `int_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `col_1` int(1) DEFAULT NULL,
  `col_2` int(20) unsigned zerofill DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from int_test;
+----+-------+----------------------+
| id | col_1 | col_2                |
+----+-------+----------------------+
|  1 |     1 | 00000000000000000001 |
| 10 |    10 | 00000000000000000010 |
+----+-------+----------------------+
2 rows in set (0.01 sec)

在上面的實驗中我們可以看到,col_2 這一列已經在左側填充了一串無意義的 0。但是在一些客戶端仍然會自動去掉左側這個無意義的 0。

由于這個顯示寬度實在雞肋,MySQL 官方也表示會在未來的版本中去掉 ZEROFILL 和顯示寬度這兩個屬性。

在現在的版本中,我們只需要了解它,并且不要用錯數據類型即可。存儲的值的范圍不超過 128/256 的話就用 TINYINT,存儲值超出 INT 的范圍的話就用 BIGINT。

下圖是各種整形數據類型的數值范圍表:

圖片描述

2. 兩個 UNSIGNED 的值無法相減?

UNSIGNED 屬性就是無符號的數字類型,如果用做自增主鍵的話,相比有符號的整形數據類型能擴展 1 倍的空間。

有的大型互聯(lián)網公司會在內部的開發(fā)規(guī)范中要求使用 UNSIGNED 的自增值。

看起來還蠻不錯,但是在使用時有一個小問題你需要注意,那就是兩個數值相減得到負數的情形。

我們再來做一個實驗,建一個兩列都帶有 UNSIGNED 屬性的表,然后再插入一行數據:

mysql> create table int_test_2 (col_1 int unsigned, col_2 int unsigned);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into int_test_2 values(1,2);
Query OK, 1 row affected (0.01 sec)

mysql> select col_1 - col_2 from int_test_2;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(`imooc_mysql_interview`.`int_test_2`.`col_1` - `imooc_mysql_interview`.`int_test_2`.`col_2`)'

在這里你可以看到,本來該輸出 - 1 的結果卻報錯了。并且錯誤看起來還有點奇怪,提示 BIGINT UNSIGNED 超出了范圍。

那為什么會發(fā)生這樣的問題呢?

其實和編程語言中的問題類似,對于有符號的整形數來說,-1 的十六進制值是 0xFFFFFFFF;而對于無符號的整形數來說,4294967295 的十六進制值也是 0xFFFFFFFF。

在 MySQL 數據庫中,對于 UNSIGNED 數的操作,它的返回值都是 UNSIGNED 的,不能是負值,所以就導致了上面的錯誤產生。

那么如果非要獲得負值呢?只需要改一下 SQL_MODE 即可:

mysql> SET sql_mode = 'NO_UNSIGNED_SUBTRACTION';
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> show warnings;
+---------+------+------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                        |
+---------+------+------------------------------------------------------------------------------------------------+
| Warning | 3090 | Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It will be removed in a future release. |
+---------+------+------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

但是不太建議這么做,如果需要避免這個情況還是老老實實的使用有符號的整形數據類型,如果擔心 INT 的數值范圍不夠用的話換成 BIGINT 基本也夠用了。

3. 選 TINYINT 還是 ENUM?

你在設計數據庫表結構的時候,可能會設計一些枚舉的列來存儲一些屬性值,比如用 (1,2,3) 代表蘋果、安卓、其他操作系統(tǒng)。

在數據類型的選擇上,可以存儲枚舉值的數據類型除了 TINYINT 和 ENUM,其實 VARCHAR (N) 也有很多人在用。那么這 3 種數據類型哪個最合適呢?

先放下 TINYINT 不說,我們來看 ENUM 這個數據類型,和前面一樣,我們先做個實驗,下面是一個建表語句:

mysql> CREATE TABLE smartphone (
    ->     vendor VARCHAR(40),
    ->     os_name ENUM('ios', 'android', 'other')
    -> );
Query OK, 0 rows affected (0.03 sec)

MySQL 官方在實現 ENUM 這個數據類型時,實際上是用 TINYINT 來存儲的。

可讀性也比較好,不需要映射成類似 1,2,3 這樣的數值。

但是 ENUM 這個數據類型在 MySQL 數據庫中有一點不好的地方是,字符串列表是預定義的,以后如果要添加或刪除屬性必須使用 ALTER TABLE。

即便現在 MySQL 數據庫有了 online DDL,ALTER TABLE 時不會鎖表,在大表上這也是一個很重的操作。

另外 ENUM 的數據類型還有兩個問題需要注意:

  1. 如果給 ENUM 列插入了一個無效值,則會轉換為空字符串( ’ ’ )存儲(SQL_MODE 設置在嚴格模式下會直接拋出錯誤)。

示例如下:

 mysql> insert into smartphone values('MicroSoft','WindowsPhone');
 Query OK, 1 row affected, 1 warning (0.01 sec)
 
 mysql> show warnings;
 +---------+------+----------------------------------------------+
 | Level   | Code | Message                                      |
 +---------+------+----------------------------------------------+
 | Warning | 1265 | Data truncated for column 'os_name' at row 1 |
 +---------+------+----------------------------------------------+
 1 row in set (0.00 sec)
 
 mysql> select * from smartphone;
 +-----------+---------+
 | vendor    | os_name |
 +-----------+---------+
 | MicroSoft |         |
 +-----------+---------+
 1 row in set (0.00 sec)
  1. 給 ENUM 的列進行排序操作時,是按照表定義中指定的順序,而不是根據字母表的順序。

示例如下:

 mysql> insert into smartphone values('HUAWEI','android'),('Apple','ios'),('NOKIA','other'),('XIAOMI','android');
 Query OK, 4 rows affected (0.01 sec)
 Records: 4  Duplicates: 0  Warnings: 0
 
 mysql> select * from smartphone order by os_name asc;
 +-----------+---------+
 | vendor    | os_name |
 +-----------+---------+
 | MicroSoft |         |
 | Apple     | ios     |
 | HUAWEI    | android |
 | XIAOMI    | android |
 | NOKIA     | other   |
 +-----------+---------+
 5 rows in set (0.00 sec)

由于 ENUM 實際存儲是用的 TINYINT,因此在存儲較小的數字時,建議直接使用 TINYINT 即可。

只有一些比較特殊的場景,例如星期、季節(jié)、顏色、性別等固定不變的枚舉值,才建議你選用 ENUM 這個數據類型。

那 VARCHAR (N) 能用嗎?

選擇 VARCHAR (N) 這個數據類型的好處是比較直觀。比如 “canceled”,“finished”,“delivering” 的可讀性顯然比 1,2,3 要好,但是占用的存儲空間也相應的變大。

但是話說回來,從性能角度考慮 VARCHAR (N) 會比 TINYINT 略差,主要表現在 JOIN 和 ORDER BY 的操作上。

你可以根據具體情況選用。

最后再來說說 TINYINT 這個數據類型。

首先 TINYINT 的字節(jié)數很小,只占 1 個字節(jié);其次它的查詢和 DML 的性能表現都不錯。

因此除了一些比較特殊的應用場景,再綜合上面的一些問題來看,在大多數的場景下,還是建議優(yōu)先選用 TINYINT。

4. 小結

今天,我給你介紹了 MySQL 里面最頻繁使用的整形數據類型,包括 INT 和 TINYINT。

INT 定義中的數字只是表示顯示寬度,它并不限制 MySQL 的實際存儲空間。因此在開發(fā)工作中不要把 INT (1) 當初 TINYINT 來用,也千萬不要把 INT (20) 當成 BIGINT 用。

對于 UNSIGNED 這個屬性,最適合的場景就是主鍵自增值,或者一些不允許出現負數的應用中。在使用時要注意帶有 UNSIGNED 的字段,在做計算時如果出現負數可能會報錯。

最后我還給你介紹了 TINYINT、ENUM、VARCHAR (N) 在存儲枚舉值的情況。鑒于前面描述的 ENUM 和 VARCHAR (N) 的一些情況,因此我的建議是首選 TINYINT,特殊場景適當選用其他類型。

參考文獻:

  1. MySQL 官方文檔:整形數據類型,https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
  2. 《MySQL 技術內幕:SQL 編程》
}
立即訂閱 ¥ 48.00

你正在閱讀課程試讀內容,訂閱后解鎖課程全部內容

千學不如一看,千看不如一練

手機
閱讀

掃一掃 手機閱讀

MySQL 開發(fā)高頻面試題精選
立即訂閱 ¥ 48.00

舉報

0/150
提交
取消