> For the complete documentation index, see [llms.txt](https://bohans.gitbook.io/ji-chu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bohans.gitbook.io/ji-chu/mysql/sql-you-hua/schema-she-ji/xuan-ze-shu-ju-lei-xing.md).

# 选择数据类型

## 数据类型选择的原则

### <mark style="color:orange;">**更小的通常更好**</mark>

一般来说，<mark style="color:blue;">**尽量使用能够正确存储和表示数据的最小数据类型**</mark>。更小的数据类型通常**更快**，因为它们占用的磁盘、内存和 CPU 缓存的**空间更少**，并且处理时需要的 **CPU 周期也更少**。

### <mark style="color:orange;">**简单为好**</mark>

<mark style="color:blue;">**简单数据类型的操作通常需要更少的 CPU 周期**</mark>。

例如，整型数据比字符型数据的比较操作代价更低，因为字符集和排序规则（collation）使字符型数据的比较更复杂。

这里有两个例子：

* **应该将日期和时间存储为 MySQL 的内置类型而不是字符串类型；**
* **应该用整型数据存储 IP 地址。**

<details>

<summary><mark style="color:purple;"><strong>IPv4 地址的存储（无符号整数）</strong></mark></summary>

人们通常使用 VARCHAR(15) 列来存储 IP 地址。然而，IP 地址实际上是 32 位无符号整数，而不是字符串。用小数点将地址分成四段的表示方法只是为了让人们阅读容易，所以<mark style="color:orange;">**应该将 IP 地址存储为无符号整数**</mark>。

MySQL 提供了 <mark style="color:blue;">**INET\_ATON()**</mark> 和 <mark style="color:blue;">**INET\_NTOA()**</mark> 函数来在这两种表示形式（整数和字符串）之间进行转换。使用的空间从 VARCHAR(15) 的约 16 字节缩减到无符号 32 位整数的 4 字节。

<mark style="color:blue;">**如果担心数据库的可读性，不想继续使用函数查看行数据，可以使用视图来简化数据查看的复杂性。**</mark>

```sql
-- 准备数据
CREATE TABLE ip_test(
    ip INT UNSIGNED
);
INSERT INTO ip_test VALUES (INET_ATON("192.168.10.110"));

-- 查询
SELECT INET_NTOA(ip) FROM ip_test;
```

```
+--------------+
|INET_NTOA(ip) |
+--------------+
|192.168.10.110|
+--------------+
```

</details>

### <mark style="color:orange;">**尽量避免存储 NULL**</mark>

即使应用程序本身并不需要存储 NULL（缺失值），很多表也包含可为 NULL 的列，这是因为可为 NULL 是列的默认属性。<mark style="color:blue;">**通常情况下最好指定列为 NOT NULL，除非明确需要存储 NULL 值**</mark>。

如果查询中包含可为 NULL 的列，对 MySQL 来说**更难优化**，因为可为 NULL 的列使得**索引、索引统计和值比较都更复杂**。可为 NULL 的列会使用**更多的存储空间**，在 MySQL 里也**需要特殊处理**。

> 通常把可为 NULL 的列改为 NOT NULL 带来的性能提升比较小，所以（调优时）没有必要首先在现有 schema 中查找并修改这种情况，除非确定这会导致问题。

## 数据类型选择的步骤

### <mark style="color:orange;">1. 确定合适的大类型</mark>

在为列选择数据类型时，<mark style="color:blue;">**第一步需要确定合适的大类型**</mark>：数字、字符串、时间等。

### <mark style="color:orange;">2. 选择具体类型</mark>

<mark style="color:blue;">**下一步是选择具体类型**</mark>。很多 MySQL 数据类型可以存储相同类型的数据，但在**存储的值范围**、**表示的精度**或者需要的**物理空间（磁盘和内存）**&#x4E0A;存在着差异。相同大类型的不同子数据类型有时也有一些**特殊的行为和属性**。

例如，DATETIME 和 TIMESAMP 列可以存储相同类型的数据：时间和日期，精确到秒。然而 TIMESTAMP 只使用 DATETIME 一半的存储空间，还会根据时区变化，而且具有特殊的自动更新能力。另一方面，TIMESTAMP 允许的时间范围要小得多，有时候它的特殊能力会成为障碍。
