#### 3.3.4.5 日期計算
MySQL 提供了幾個函數可以用來完成日期計算, 例如, 計算年齡或者提取部分日期.
要確定每個寵物的年齡, 可以使用 [`TIMESTAMPDIFF()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_timestampdiff) 函數. 它的參數是你想要表達結果的單位, 以及取差的兩個日期. 下面的查詢顯示了每只寵物的出生日期, 當前日期和年齡. ***`alias`*** (`age`) 用于讓最終的輸出列名更有意義.
```sql
mysql> SELECT name, birth, CURDATE(),
TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
FROM pet;
+----------+------------+------------+------+
| name | birth | CURDATE() | age |
+----------+------------+------------+------+
| Fluffy | 1993-02-04 | 2003-08-19 | 10 |
| Claws | 1994-03-17 | 2003-08-19 | 9 |
| Buffy | 1989-05-13 | 2003-08-19 | 14 |
| Fang | 1990-08-27 | 2003-08-19 | 12 |
| Bowser | 1989-08-31 | 2003-08-19 | 13 |
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| Whistler | 1997-12-09 | 2003-08-19 | 5 |
| Slim | 1996-04-29 | 2003-08-19 | 7 |
| Puffball | 1999-03-30 | 2003-08-19 | 4 |
+----------+------------+------------+------+
```
查詢可以正常工作, 但是如果按照某種順序顯示行, 則可以更容易的掃描結果. 可以通過添加 `ORDER BY name` 子句按照名稱排序來輸出:
```sql
mysql> SELECT name, birth, CURDATE(),
TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
FROM pet ORDER BY name;
+----------+------------+------------+------+
| name | birth | CURDATE() | age |
+----------+------------+------------+------+
| Bowser | 1989-08-31 | 2003-08-19 | 13 |
| Buffy | 1989-05-13 | 2003-08-19 | 14 |
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| Claws | 1994-03-17 | 2003-08-19 | 9 |
| Fang | 1990-08-27 | 2003-08-19 | 12 |
| Fluffy | 1993-02-04 | 2003-08-19 | 10 |
| Puffball | 1999-03-30 | 2003-08-19 | 4 |
| Slim | 1996-04-29 | 2003-08-19 | 7 |
| Whistler | 1997-12-09 | 2003-08-19 | 5 |
+----------+------------+------------+------+
```
按照 `age` 而不是 `name` 進行排序輸出, 只需要使用不同的 `ORDER BY` 子句:
```sql
mysql> SELECT name, birth, CURDATE(),
TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
FROM pet ORDER BY age;
+----------+------------+------------+------+
| name | birth | CURDATE() | age |
+----------+------------+------------+------+
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| Puffball | 1999-03-30 | 2003-08-19 | 4 |
| Whistler | 1997-12-09 | 2003-08-19 | 5 |
| Slim | 1996-04-29 | 2003-08-19 | 7 |
| Claws | 1994-03-17 | 2003-08-19 | 9 |
| Fluffy | 1993-02-04 | 2003-08-19 | 10 |
| Fang | 1990-08-27 | 2003-08-19 | 12 |
| Bowser | 1989-08-31 | 2003-08-19 | 13 |
| Buffy | 1989-05-13 | 2003-08-19 | 14 |
+----------+------------+------------+------+
```
相似的查詢可以用來確定已死亡動物的死亡年齡. 你可以通過那些 `death` 的值為 `NULL` 來確定動物是那些. 然后, 對那些非 `NULL` 值, 計算出 `death` 和 `birth` 的差值:
```sql
mysql> SELECT name, birth, death,
TIMESTAMPDIFF(YEAR,birth,death) AS age
FROM pet WHERE death IS NOT NULL ORDER BY age;
+--------+------------+------------+------+
| name | birth | death | age |
+--------+------------+------------+------+
| Bowser | 1989-08-31 | 1995-07-29 | 5 |
+--------+------------+------------+------+
```
查詢使用 `death IS NOT NULL` 而不是 `death <> NULL` 是因為 `NULL` 是一個特殊值, 不能使用常規的比較操作符來操作. 稍后將進行討論. 參閱 [Section 3.3.4.6, “處理 NULL 值”](https://dev.mysql.com/doc/refman/8.0/en/working-with-null.html).
如果你想知道下個月那些動物過生日呢? 對于這類運算, 年月日無關緊要; 你只需取出 `birth` 列的月份部分. MySQL 提供了幾個提取日期部分的函數, 比如 [`YEAR()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_year), [`MONTH()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month), 和 [`DAYOFMONTH()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_dayofmonth). [`MONTH()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month) 函數適合這里. 要查看它是如何工作的, 運行一個顯示出 `birth` 和 [`MONTH(birth)`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month) 的簡單查詢:
```sql
mysql> SELECT name, birth, MONTH(birth) FROM pet;
+----------+------------+--------------+
| name | birth | MONTH(birth) |
+----------+------------+--------------+
| Fluffy | 1993-02-04 | 2 |
| Claws | 1994-03-17 | 3 |
| Buffy | 1989-05-13 | 5 |
| Fang | 1990-08-27 | 8 |
| Bowser | 1989-08-31 | 8 |
| Chirpy | 1998-09-11 | 9 |
| Whistler | 1997-12-09 | 12 |
| Slim | 1996-04-29 | 4 |
| Puffball | 1999-03-30 | 3 |
+----------+------------+--------------+
```
找出下個月有生日的動物非常簡單. 假設當前月份是四月. 然后這個月份的值就是 `4`, 你可以這樣查找 `5` 月份出生的動物:
```sql
mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name | birth |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+
```
如果當前月份是 12 月, 就會出現一個小問題. 你不能僅僅在 (`12`) 上面加 1, 然后尋找第 `13` 個月出生的動物, 因為沒有這個月份. 相反, 你要尋找 `1` 月的動物.
你可以寫個查詢無論在哪個月份都可以正常工作, 這樣你就不必為特定的月份使用數字. [`DATE_ADD()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add) 允許向指定日期添加間隔. 如果添加一個月到 [`CURDATE()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_curdate) 的值中, 然后使用 [`MONTH()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month) 提取月份部分, 然后產生了尋找生日的月份:
```sql
mysql> SELECT name, birth FROM pet
WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));
```
另一種完成任務的方法是使用 `MOD` 方法對其取模, 如果當前月份是 `12`, 那么取值為 `0`, 加 `1` 來獲得當前月份的下一個月的值:
```sql
mysql> SELECT name, birth FROM pet
WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;
```
[`MONTH()`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month) 返回 `1` 到 `12` 之間的數字. [`MOD(something,12)`](https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_mod) 返回 `0` 和 `11` 之間的數字. 因此, 必須在 [`MOD()`](https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_mod) 之后添加, 否則將從 `11` 月份直接到 `1` 月份.
如果使用無效日期, 則會計算失敗并產生警告:
```sql
mysql> SELECT '2018-10-31' + INTERVAL 1 DAY;
+-------------------------------+
| '2018-10-31' + INTERVAL 1 DAY |
+-------------------------------+
| 2018-11-01 |
+-------------------------------+
mysql> SELECT '2018-10-32' + INTERVAL 1 DAY;
+-------------------------------+
| '2018-10-32' + INTERVAL 1 DAY |
+-------------------------------+
| NULL |
+-------------------------------+
mysql> SHOW WARNINGS;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1292 | Incorrect datetime value: '2018-10-32' |
+---------+------+----------------------------------------+
```
- 簡介
- 前言和法律條款
- 安裝和更新 MySQL
- 在 Linux 上安裝 MySQL
- 在 Linux 上使用 APT 庫安裝 MySQL
- 在 Linux 上使用 Docker 部署 MySQL
- 使用 Docker 部署 MySQL 服務器的基本步驟
- 使用 Docker 部署 MySQL 服務器的更多主題
- 教程
- 連接到服務器和從服務器斷開
- 輸入查詢
- 創建和使用數據庫
- 創建和選擇數據庫
- 創建表
- 將數據加載到表中
- 從表中檢索數據
- 選擇所有數據
- 選擇特定行
- 選擇指定列
- 行排序
- 日期計算
- 處理 NULL 值
- 模式匹配
- 計算行數
- 使用多個表
- 獲取數據庫和表的信息
- 在批處理模式使用 mysql
- 常見查詢示例
- 列的最大值
- 包含某一行最大值的記錄
- 每組中列的最大值
- 擁有某個字段的組間最大值的行
- 使用用戶自定義變量
- 使用外鍵
- 兩個鍵上搜索
- 計算每日訪問量
- 使用 AUTO_INCREMENT
- 在 Apache 中使用 MySQL
- MySQL 程序
- MySQL 客戶端程序
- mysql — MySQL 命令行客戶端
- 優化
- 優化概述
- 優化 SQL 語句
- 優化和索引
- 優化數據庫結構
- 優化 InnoDB 表
- 優化 MyISAM 表
- 優化 MEMORY 表
- 理解查詢執行計劃
- 控制查詢優化器
- 緩沖和緩存
- 優化鎖操作
- 優化 MySQL 服務器
- 測量性能 (Benchmarking)
- 檢查線程信息