5.3.4.5 日期计算

MySQL 提供了多个函数,您可以使用这些函数对日期执行计算,例如,计算年龄或提取日期的各个部分。

要确定您的每只宠物的年龄,请使用 TIMESTAMPDIFF() 函数。它的参数是您希望结果以其表达的单位以及要计算差值的两个日期。以下查询显示了每个宠物的出生日期、当前日期和年龄(以年为单位)。使用了 别名 (age) 使最终输出列标签更有意义。

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 子句以名称排序输出来完成此操作。

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 子句即可。

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 值的动物,计算 deathbirth 值之间的差值。

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 是一个特殊值,不能使用常规比较运算符进行比较。这将在后面讨论。请参阅 第 5.3.4.6 节,“处理 NULL 值”

如果您想知道哪些动物将在下个月过生日?对于这种类型的计算,年和日无关紧要;您只需要提取 birth 列的月份部分。MySQL 提供了多个函数用于提取日期的各个部分,例如 YEAR()MONTH()DAYOFMONTH()。在这里,MONTH() 是合适的函数。要查看它的工作原理,请运行一个简单的查询,该查询显示 birthMONTH(birth) 的值。

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 月。然后月份值为 4,您可以查找出生于 5 月(月份 5)的动物,如下所示。

mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name  | birth      |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+

如果当前月份为 12 月,则会出现一个小问题。您不能简单地将月份数 (12) 加 1,然后查找出生于月份 13 的动物,因为不存在这样的月份。相反,您需要查找出生于 1 月(月份 1)的动物。

您可以编写查询,使其无论当前月份是什么都能正常工作,因此您不必使用特定月份的数字。 DATE_ADD() 使您能够将时间间隔添加到给定日期。如果您将一个月添加到 CURDATE() 的值中,然后使用 MONTH() 提取月份部分,结果将生成要查找生日的月份。

mysql> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

完成相同任务的另一种方法是在使用模运算符 (MOD) 将月份值包装到 0(如果当前为 12)后,加 1 以获得当前月份后的下一个月份。

mysql> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;

MONTH() 返回 112 之间的数字。而 MOD(something,12) 返回 011 之间的数字。因此,加法必须在 MOD() 之后,否则我们将从 11 月 (11) 变为 1 月 (1)。

如果计算使用无效日期,则计算将失败并产生警告。

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' |
+---------+------+----------------------------------------+