当前位置:网站首页>【MySQL 使用秘籍】一網打盡 MySQL 時間和日期類型與相關操作函數(三)
【MySQL 使用秘籍】一網打盡 MySQL 時間和日期類型與相關操作函數(三)
2022-07-05 13:20:00 【TakingCoding4Granted】
文章目錄
建議首先閱讀前序兩篇博文:
1. 實現日期和天或時間和秒之間的轉換
問題
你希望將日期或時間轉化為更基本的單比特,例如將日期轉換為天數,將時間轉換為秒數。通常,這對於進行日期或時間的計算非常有幫助。
解决方案
轉換的方式取决於待轉換值的類型:
- 實現時間和秒數之間的轉換,使用
TIME_TO_SEC()
以及SEC_TO_TIME()
函數; - 實現日期和天數之間的轉換,使用
TO_DAYS()
以及FROM_DAYS()
函數; - 時間日期+時間和秒數之間的轉換,使用
UNIX_TIMESTAMP()
或FROM_UNIXTIME()
函數。
討論
在下面的討論中,介紹了如何進行幾種不同日期(時間)和天(秒)之間的相互轉換。
時間和秒相互轉換
如上所述,可以使用 TIME_TO_SEC()
以及 SEC_TO_TIME()
函數。
函數 TIME_TO_SEC()
將一個時間值轉換為對應的秒數,反之,則使用函數 SEC_TO_TIME()
。
mysql> SELECT t1,
-> TIME_TO_SEC(t1) AS 'TIME to seconds',
-> SEC_TO_TIME(TIME_TO_SEC(t1)) AS 'TIME to seconds to TIME'
-> FROM time_val;
+----------+-----------------+-------------------------+
| t1 | TIME to seconds | TIME to seconds to TIME |
+----------+-----------------+-------------------------+
| 15:00:00 | 54000 | 15:00:00 |
| 05:01:30 | 18090 | 05:01:30 |
| 12:30:20 | 45020 | 12:30:20 |
+----------+-----------------+-------------------------+
3 rows in set (0.00 sec)
如果希望將時間值轉換為以分鐘,小時或天為單比特,則需要進行對應的除法運算:
mysql> SELECT t1,
-> TIME_TO_SEC(t1) AS 'seconds',
-> TIME_TO_SEC(t1)/60 AS 'minutes',
-> TIME_TO_SEC(t1)/(60*60) AS 'hours',
-> TIME_TO_SEC(t1)/(24*60*60) AS 'days'
-> FROM time_val;
+----------+---------+----------+---------+--------+
| t1 | seconds | minutes | hours | days |
+----------+---------+----------+---------+--------+
| 15:00:00 | 54000 | 900.0000 | 15.0000 | 0.6250 |
| 05:01:30 | 18090 | 301.5000 | 5.0250 | 0.2094 |
| 12:30:20 | 45020 | 750.3333 | 12.5056 | 0.5211 |
+----------+---------+----------+---------+--------+
3 rows in set (0.00 sec)
如果你不希望保留小數比特,那麼你可以對上述結果使用 FLOOR()
函數。
如果你在使用函數 TIME_TO_SEC()
時傳入了一個日期+時間格式的值,那麼該函數會將時間部分提取出來,然後丟弃日期部分。實際上,這也可以被當做從 DATETIME
或 TIMESTAMP
提取時間部分的另一種方式。例如:
mysql> SELECT dt,
-> TIME_TO_SEC(dt) AS 'time part in seconds',
-> SEC_TO_TIME(TIME_TO_SEC(dt)) AS 'time part as TIME'
-> FROM datetime_val;
+---------------------+----------------------+-------------------+
| dt | time part in seconds | time part as TIME |
+---------------------+----------------------+-------------------+
| 1970-01-01 00:00:00 | 0 | 00:00:00 |
| 1999-12-31 09:00:00 | 32400 | 09:00:00 |
| 2000-06-04 15:45:30 | 56730 | 15:45:30 |
| 2017-03-16 12:30:15 | 45015 | 12:30:15 |
+---------------------+----------------------+-------------------+
4 rows in set (0.00 sec)
日期和天相互轉換
如上所述,如果你有一個時間值,但是你希望將其和天數之間相互轉換,你可以使用 TO_DAYS()
以及 FROM_DAYS()
函數。
mysql> SELECT d,
-> TO_DAYS(d) AS 'DATE to days',
-> FROM_DAYS(TO_DAYS(d)) AS 'DATE to days to DATE'
-> FROM date_val;
+------------+--------------+----------------------+
| d | DATE to days | DATE to days to DATE |
+------------+--------------+----------------------+
| 1864-02-28 | 680870 | 1864-02-28 |
| 1900-01-15 | 693975 | 1900-01-15 |
| 1999-12-31 | 730484 | 1999-12-31 |
| 2000-06-04 | 730640 | 2000-06-04 |
| 2017-03-16 | 736769 | 2017-03-16 |
+------------+--------------+----------------------+
5 rows in set (0.00 sec)
類似地,如果你為 TO_DAYS()
函數傳入日期+時間值,那麼該函數會將日期部分提取出來,然後丟弃時間部分。例如:
mysql> SELECT dt,
-> TO_DAYS(dt) AS 'date part in days',
-> FROM_DAYS(TO_DAYS(dt)) AS 'date part as DATE'
-> FROM datetime_val;
+---------------------+-------------------+-------------------+
| dt | date part in days | date part as DATE |
+---------------------+-------------------+-------------------+
| 1970-01-01 00:00:00 | 719528 | 1970-01-01 |
| 1999-12-31 09:00:00 | 730484 | 1999-12-31 |
| 2000-06-04 15:45:30 | 730640 | 2000-06-04 |
| 2017-03-16 12:30:15 | 736769 | 2017-03-16 |
+---------------------+-------------------+-------------------+
4 rows in set (0.00 sec)
日期+時間和秒相互轉換
對於 DATETIME
和 TIMESTAMP
類型的值,如果這兩個類型的值在 TIMESTAMP
類型的範圍內,即從 1970 年到 2038 年,那麼可以使用 UNIX_TIMESTAMP()
和 FROM_UNIXTIME()
來實現二者和秒數之間的相互轉換。例如:
mysql> SELECT dt,
-> UNIX_TIMESTAMP(dt) AS seconds,
-> FROM_UNIXTIME(UNIX_TIMESTAMP(dt)) AS timestamp
-> FROM datetime_val;
+---------------------+------------+---------------------+
| dt | seconds | timestamp |
+---------------------+------------+---------------------+
| 1970-01-01 00:00:00 | 0 | 1970-01-01 08:00:00 |
| 1999-12-31 09:00:00 | 946602000 | 1999-12-31 09:00:00 |
| 2000-06-04 15:45:30 | 960104730 | 2000-06-04 15:45:30 |
| 2017-03-16 12:30:15 | 1489638615 | 2017-03-16 12:30:15 |
+---------------------+------------+---------------------+
4 rows in set (0.00 sec)
函數 UNIX_TIMESTAMP()
也可以將 DATE
類型的值轉換為秒數,該函數默認這些日期的時間部分都是 00:00:00 :
mysql> SELECT
-> CURDATE(),
-> UNIX_TIMESTAMP(CURDATE()),
-> FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE()));
+------------+---------------------------+------------------------------------------+
| CURDATE() | UNIX_TIMESTAMP(CURDATE()) | FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE())) |
+------------+---------------------------+------------------------------------------+
| 2022-07-02 | 1656691200 | 2022-07-02 00:00:00 |
+------------+---------------------------+------------------------------------------+
1 row in set (0.00 sec)
2. 計算日期或時間的間隔
問題
你希望知道兩個日期或時間之間的間隔。
解决方案
想要計算兩個日期或時間之間的間隔,你可以使用專門的函數,也可以先將日期(時間)轉換為天(秒),然後計算二者之間的間隔。至於使用具體哪一個函數,這主要取决於你想要計算的兩個值究竟是日期還是時間類型。
討論
下面介紹了幾種用於計算日期(時間)間隔的方式。
使用日期(時間)函數計算間隔
想要計算兩個日期值之間間隔的天數,可以使用 DATEDIFF()
函數:
mysql> SET @d1 = '2022-01-01', @d2 = '2021-12-01';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT DATEDIFF(@d1, @d2) AS 'd1 - d2', DATEDIFF(@d2, @d1) AS 'd2 - d1';
+---------+---------+
| d1 - d2 | d2 - d1 |
+---------+---------+
| 31 | -31 |
+---------+---------+
1 row in set (0.00 sec)
函數 DATEDIFF()
也適用於兩個日期+時間類型的值,但是會在計算時忽略時間部分。因此,該函數適用於兩個 DATE
,DATETIME
或 TIMESTAMP
類型的值之間進行間隔天數的計算。
想要計算兩個時間值之間的間隔,可以使用 TIMEDIFF()
函數:
mysql> SET @t1 = '12:00:00', @t2 = '16:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMEDIFF(@t1, @t2) AS 't1 - t2', TIMEDIFF(@t2, @t1) AS 't2 - t1';
+------------------+-----------------+
| t1 - t2 | t2 - t1 |
+------------------+-----------------+
| -04:30:00.000000 | 04:30:00.000000 |
+------------------+-----------------+
1 row in set (0.00 sec)
函數 TIMEDIFF()
也適用於兩個日期+時間類型的值。
兩個時間值之間的間隔可以錶示為一個 TIME
類型的值,而後者又可以進一步使用 HOUR()
,MINUTE()
以及 SECOND()
函數進行拆分。例如:
mysql> SELECT t1, t2,
-> TIMEDIFF(t2, t1) AS 't2 - t1 as TIME',
-> IF(TIMEDIFF(t2, t1) >= 0,'+','-') AS sign,
-> HOUR(TIMEDIFF(t2, t1)) AS hour,
-> MINUTE(TIMEDIFF(t2, t1)) AS minute,
-> SECOND(TIMEDIFF(t2, t1)) AS second
-> FROM time_val;
+----------+----------+-----------------+------+------+--------+--------+
| t1 | t2 | t2 - t1 as TIME | sign | hour | minute | second |
+----------+----------+-----------------+------+------+--------+--------+
| 15:00:00 | 15:00:00 | 00:00:00 | + | 0 | 0 | 0 |
| 05:01:30 | 02:30:20 | -02:31:10 | - | 2 | 31 | 10 |
| 12:30:20 | 17:30:45 | 05:00:25 | + | 5 | 0 | 25 |
+----------+----------+-----------------+------+------+--------+--------+
3 rows in set (0.00 sec)
如果你希望計算兩個日期或兩個日期+時間值之間的間隔,也可以使用 TIMESTAMPDIFF()
函數。該函數比較靈活,你可以指定期望的間隔單比特。例如:
mysql> SET @dt1 = '1900-01-01 00:00:00', @dt2 = '1910-01-01 00:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT
-> TIMESTAMPDIFF(MINUTE, @dt1, @dt2) AS minutes,
-> TIMESTAMPDIFF(HOUR, @dt1, @dt2) AS hours,
-> TIMESTAMPDIFF(DAY, @dt1, @dt2) AS days,
-> TIMESTAMPDIFF(WEEK, @dt1, @dt2) AS weeks,
-> TIMESTAMPDIFF(YEAR, @dt1, @dt2) AS years;
+---------+-------+------+-------+-------+
| minutes | hours | days | weeks | years |
+---------+-------+------+-------+-------+
| 5258880 | 87648 | 3652 | 521 | 10 |
+---------+-------+------+-------+-------+
1 row in set (0.00 sec)
實際上,函數 TIMESTAMPDIFF()
除了支持上述間隔單比特外,還支持 MICROSECOND
,MONTH
,QUARTER
。此外,函數 TIMESTAMP()
可以用來很方便地計算出一個人的年齡。
對於函數 TIMESTAMPDIFF()
,有以下兩點需要特別注意:
- 如果第一個日期(時間)值大於第二個,那麼結果將為負數,這和
DATEDIFF()
以及TIMEDIFF()
函數剛好相反; - 雖然函數
TIMESTAMPDIFF()
的函數名中包含TIMESTAMP
,但是其參數的範圍卻不受TIMESTAMP
類型取值範圍的限制。
使用基本單比特計算間隔
計算兩個日期(時間)之間間隔的另一種方式是使用如:秒和天在內的基本單比特:
- 將兩個日期(時間)轉換為天或秒;
- 使用上述兩個轉換後的值進行間隔計算;
- 將以天為秒為單比特的計算結果轉回日期或時間。
根據希望計算間隔的是日期還是時間,需要使用不同的轉換函數:
- 使用函數
TIME_TO_SEC()
和SEC_TO_TIME()
進行時間和秒之間的轉換; - 使用函數
TO_DAYS()
和FROM_DAYS()
進行日期和天數之間的轉換; - 使用函數
UNIX_TIMESTAMP()
和FROM_UNIXTIME()
進行日期+時間和秒之間的轉換。
使用秒計算時間間隔
mysql> SELECT t1, t2,
-> TIME_TO_SEC(t2) - TIME_TO_SEC(t1) AS 't2 - t1 (in seconds)',
-> SEC_TO_TIME(TIME_TO_SEC(t2) - TIME_TO_SEC(t1)) AS 't2 - t1 (as TIME)'
-> FROM time_val;
+----------+----------+----------------------+-------------------+
| t1 | t2 | t2 - t1 (in seconds) | t2 - t1 (as TIME) |
+----------+----------+----------------------+-------------------+
| 15:00:00 | 15:00:00 | 0 | 00:00:00 |
| 05:01:30 | 02:30:20 | -9070 | -02:31:10 |
| 12:30:20 | 17:30:45 | 18025 | 05:00:25 |
+----------+----------+----------------------+-------------------+
3 rows in set (0.00 sec)
使用基本單比特計算兩個日期或日期+時間的間隔
當你希望先將兩個日期相對於同一個給定的參考時間點轉換為共同的基本單比特,然後計算二者的間隔時,日期的取值範圍决定了可用的轉換單比特:
- 對於自 1970-01-01 00:00:00 UTC 往後的
DATE
,DATETIME
或TIMESTAMP
類型,你可以先將這些類型的值轉換為以秒為單比特,以實現精確到秒級的間隔精確度; - 對於從格裏高利曆實行的開始時間即 1582 年至 1970-01-01 00:00:00 UTC 的日期,你可以先將日期轉換為以秒為單比特,以實現精確到天級別的間隔精確度;
- 對於更早的日期而言,先將日期轉換為基本單比特,然後計算間隔的方式就比較困難了。此時,需要使用編程語言的 API ,因為使用 SQL 語句會比較困難或壓根不可行。
想要計算兩個日期或日期+時間值之間間隔的天數,可以先使用 TO_DAYS()
函數將其轉換為天數,然後計算二者以天為單比特的間隔。如果希望得到二者以星期為單比特的間隔,可以再將結果除以七:
mysql> SET @days = TO_DAYS('1884-01-01') - TO_DAYS('1883-06-05');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @days AS days, @days/7 AS weeks;
+------+---------+
| days | weeks |
+------+---------+
| 210 | 30.0000 |
+------+---------+
1 row in set (0.00 sec)
需要注意的是,你不能直接通過類似的方式將天數轉換為月數或年數,因為這二者的長度不盡相同。如果想要實現這樣的效果,需要使用上面介紹的 TIMESTAMPDIFF()
函數。
針對介於 TIMESTAMP
類型範圍內,即介於 1970 至 2038 年之間的日期+時間值,你可以通過使用 UNIX_TIMESTAMP()
函數來實現兩個這樣日期+時間值之間精確到秒級的時間間隔。想要得到以其他單比特錶示的間隔,可以很容易將秒數轉換為分鐘數,小時數,天數或星期數。例如:
mysql> SET @dt1 = '1984-01-01 09:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @dt2 = @dt1 + INTERVAL 14 DAY;
Query OK, 0 rows affected (0.00 sec)
mysql> SET @interval = UNIX_TIMESTAMP(@dt2) - UNIX_TIMESTAMP(@dt1);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @interval AS seconds,
-> FLOOR(@interval / 60) AS minutes,
-> FLOOR(@interval / (60 * 60)) AS hours,
-> FLOOR(@interval / (24 * 60 * 60)) AS days,
-> FLOOR(@interval / (7 * 24 * 60 * 60)) AS weeks;
+---------+---------+-------+------+-------+
| seconds | minutes | hours | days | weeks |
+---------+---------+-------+------+-------+
| 1209600 | 20160 | 336 | 14 | 2 |
+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
對於 TIMESTAMP
類型可以錶示的範圍以外的值,因為無法像上述情况一樣使用 UNIX_TIMESTAMP()
函數,所以計算兩個日期+時間類型值之間間隔的計算方式更加繁瑣一點:
- 首先使用
TO_DAYS()
函數計算出二者的日期部分相差的天數,然後乘以 24 × 60 × 60 得到秒數; - 接著使用
TIME_TO_SEC()
函數計算出二者時間部分相差的秒數。
mysql> SET @dt1 = '1800-02-14 07:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @dt2 = '1800-02-17 06:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @interval =
-> ((TO_DAYS(@dt2) - TO_DAYS(@dt1)) * 24*60*60)
-> + TIME_TO_SEC(@dt2) - TIME_TO_SEC(@dt1);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @interval AS seconds, SEC_TO_TIME(@interval) AS TIME;
+---------+----------+
| seconds | TIME |
+---------+----------+
| 255600 | 71:00:00 |
+---------+----------+
1 row in set (0.00 sec)
3. 日期或時間相加
問題
你希望實現日期或時間相加。例如,你希望將給定的秒數加到一個時間值上,或者你希望確定從今天往後三周的日期。
解决方案
實現日期或時間相加,你有以下幾個選擇:
- 使用專用的函數;
- 使用
+ INTERVAL
或- INTERVAL
操作符; - 先將對應值轉換為基本單比特,然後進行相加求和。
需要注意的是,具體適用的函數或操作符取决於日期或時間值的類型。
討論
使用專用函數或操作符實現日期或時間相加
想要將一個時間類型值加到另一個時間類型值或日期+時間類型值上,可以使用 ADDTIME()
函數:
mysql> SELECT ADDTIME(@t1, @t2);
+-------------------+
| ADDTIME(@t1, @t2) |
+-------------------+
| 27:30:00 |
+-------------------+
1 row in set (0.00 sec)
mysql> SET @dt = '1984-03-01 12:00:00', @t = '12:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT ADDTIME(@dt, @t);
+---------------------+
| ADDTIME(@dt, @t) |
+---------------------+
| 1984-03-02 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
想要將一個時間類型值加到另一個日期類型值或日期+時間類型值上,可以使用 TIMESTAMP()
函數:
mysql> SET @d = '1984-03-01', @t = '15:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMESTAMP(@d, @t);
+----------------------------+
| TIMESTAMP(@d, @t) |
+----------------------------+
| 1984-03-01 15:30:00.000000 |
+----------------------------+
1 row in set (0.00 sec)
mysql> SET @dt = '1984-03-01 12:00:00', @t = '12:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMESTAMP(@dt, @t);
+----------------------------+
| TIMESTAMP(@dt, @t) |
+----------------------------+
| 1984-03-02 00:00:00.000000 |
+----------------------------+
1 row in set (0.00 sec)
MySQL 還提供了 DATE_ADD()
和 DATE_SUB()
兩個函數,這兩個函數可用於將一個時間段加到一個日期上,或者從一個日期上减去一個時間段。每個函數都接受一個日期或日期+時間類型的值 d
以及一個時間段作為參數,即函數的使用語法如下:
DATE_ADD(d, INTERVAL val unit)
DATE_SUB(d, INTERVAL val unit)
操作符 + INTERVAL
和 - INTERVAL
可以實現類似功能:
d + INTERVAL val unit
d - INTERVAL val unit
其中 _unit
_是時間間隔的單比特,而 val
是時間間隔的數量。常見的單比特有 SECOND
,MINUTE
,HOUR
,DAY
,MONTH
以及 YEAR
。
- 得到從今天往後三天的日期:
mysql> SELECT CURDATE(), DATE_ADD(CURDATE(), INTERVAL 3 DAY);
+------------+-------------------------------------+
| CURDATE() | DATE_ADD(CURDATE(), INTERVAL 3 DAY) |
+------------+-------------------------------------+
| 2022-07-03 | 2022-07-06 |
+------------+-------------------------------------+
1 row in set (0.00 sec)
- 得到從今天往後一周的日期:
mysql> SELECT CURDATE(), DATE_SUB(CURDATE(), INTERVAL 1 WEEK);
+------------+--------------------------------------+
| CURDATE() | DATE_SUB(CURDATE(), INTERVAL 1 WEEK) |
+------------+--------------------------------------+
| 2022-07-03 | 2022-06-26 |
+------------+--------------------------------------+
1 row in set (0.00 sec)
- 得到從現在往後 60 個小時的日期和時間:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL 60 HOUR);
+---------------------+-----------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL 60 HOUR) |
+---------------------+-----------------------------------+
| 2022-07-03 00:27:09 | 2022-07-05 12:27:09 |
+---------------------+-----------------------------------+
1 row in set (0.00 sec)
- 得到從現在往後 14.5 個小時的日期和時間:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL '14:30' HOUR_MINUTE);
+---------------------+-----------------------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL '14:30' HOUR_MINUTE) |
+---------------------+-----------------------------------------------+
| 2022-07-03 00:28:47 | 2022-07-03 14:58:47 |
+---------------------+-----------------------------------------------+
1 row in set (0.00 sec)
- 得到從現在往後 3 天 4 個小時的日期和時間:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL '3 4' DAY_HOUR);
+---------------------+------------------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL '3 4' DAY_HOUR) |
+---------------------+------------------------------------------+
| 2022-07-03 00:30:14 | 2022-07-06 04:30:14 |
+---------------------+------------------------------------------+
1 row in set (0.00 sec)
實際上,函數 DATE_ADD()
和 DATE_SUB()
是可互換使用的,例如,下面兩個錶達式實際上是等價的:
DATE_ADD(d, INTERVAL -3 MONTH)
DATE_SUB(d, INTERVAL 3 MONTH)
類似地,除了使用函數 DATE_ADD()
和 DATE_SUB()
,你也可以使用操作符 + INTERVAL
和 - INTERVAL
實現相同的功能:
mysql> SELECT CURDATE(), CURDATE() + INTERVAL 1 YEAR;
+------------+-----------------------------+
| CURDATE() | CURDATE() + INTERVAL 1 YEAR |
+------------+-----------------------------+
| 2022-07-03 | 2023-07-03 |
+------------+-----------------------------+
1 row in set (0.00 sec)
mysql> SELECT NOW(), NOW() - INTERVAL '1 12' DAY_HOUR;
+---------------------+----------------------------------+
| NOW() | NOW() - INTERVAL '1 12' DAY_HOUR |
+---------------------+----------------------------------+
| 2022-07-03 00:36:58 | 2022-07-01 12:36:58 |
+---------------------+----------------------------------+
1 row in set (0.00 sec)
函數 TIMESTAMPADD()
也可以實現將一個時間段加到日期或日期+時間的值上,其使用的語法也和函數 DATE_ADD()
比較類似:
TIMESTAMPADD(unit,interval,d) = DATE_ADD(d,INTERVAL interval unit)
4. 計算某月的第一天、最後一天或天數
問題
給定一個日期,你希望確定該日期所在月份的第一天或最後一天日期,或者在該月份 n 個月之後的月份中第一天或最後一天的日期,以及某個月份中一共有多少天。
解决方案
為了得到某個日期所在月份第一天的日期值,需要使用使用日期平移計算。為了得到某個日期所在月份的最後一天的日期值,可以直接使用 LAST_DAY()
函數。為了得到某個月份的天數,可以先得到該月份最後一天的日期值,然後對其使用 DAYOFMONTH()
函數即可。
討論
有時候,你可能有一個參考日期,並且你希望得到一個目標日期值,但是二者之間並沒有固定的相對關系。例如,當前日期到本月第一天和最後一天的天數並非固定的天數。
為了得到給定日期值所在月份第一天的日期,可以將給定日期向前平移比 DAYOFMONTH()
少 1 的天數即可:
mysql> SELECT d, DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY) AS '1st of month'
-> FROM date_val;
+------------+--------------+
| d | 1st of month |
+------------+--------------+
| 1864-02-28 | 1864-02-01 |
| 1900-01-15 | 1900-01-01 |
| 1999-12-31 | 1999-12-01 |
| 2000-06-04 | 2000-06-01 |
| 2017-03-16 | 2017-03-01 |
+------------+--------------+
5 rows in set (0.00 sec)
更一般地,如果希望得到距離給定日期 n 個月之後的日期所在月份的第一天的日期值,可以使用下列 SQL 語句:
DATE_ADD(DATE_SUB(d,INTERVAL DAYOFMONTH(d)-1 DAY),INTERVAL n MONTH)
例如,針對給定日期,為了確定該日期前一個月和後一個月的第一天的日期,可以使用下列 SQL 語句:
mysql> SELECT d,
-> DATE_ADD(DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY), INTERVAL -1 MONTH)
-> AS '1st of previous month',
-> DATE_ADD(DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY), INTERVAL 1 MONTH)
-> AS '1st of following month'
-> FROM date_val;
+------------+-----------------------+------------------------+
| d | 1st of previous month | 1st of following month |
+------------+-----------------------+------------------------+
| 1864-02-28 | 1864-01-01 | 1864-03-01 |
| 1900-01-15 | 1899-12-01 | 1900-02-01 |
| 1999-12-31 | 1999-11-01 | 2000-01-01 |
| 2000-06-04 | 2000-05-01 | 2000-07-01 |
| 2017-03-16 | 2017-02-01 | 2017-04-01 |
+------------+-----------------------+------------------------+
5 rows in set (0.00 sec)
想要得到某給定日期所在月份最後一天的日期相對而言就簡單很多,因為可以直接使用函數 LAST_DAY()
:
mysql> SELECT d, LAST_DAY(d) AS 'last of month'
-> FROM date_val;
+------------+---------------+
| d | last of month |
+------------+---------------+
| 1864-02-28 | 1864-02-29 |
| 1900-01-15 | 1900-01-31 |
| 1999-12-31 | 1999-12-31 |
| 2000-06-04 | 2000-06-30 |
| 2017-03-16 | 2017-03-31 |
+------------+---------------+
5 rows in set (0.00 sec)
類似地,更一般地,如果希望得到距離給定日期 n 個月之後的日期所在月份的最後一天的日期值,可以使用下列 SQL 語句:
LAST_DAY(DATE_ADD(d, INTERVAL n MONTH))
例如,針對給定的日期,想要得到相對該日期一個月前和一個月後的日期所在月最後一天的日期,可以使用下列 SQL 語句:
mysql> SELECT d,
-> LAST_DAY(DATE_ADD(d, INTERVAL -1 MONTH))
-> AS 'last of previous month',
-> LAST_DAY(DATE_ADD(d, INTERVAL 1 MONTH))
-> AS 'last of following month'
-> FROM date_val;
+------------+------------------------+-------------------------+
| d | last of previous month | last of following month |
+------------+------------------------+-------------------------+
| 1864-02-28 | 1864-01-31 | 1864-03-31 |
| 1900-01-15 | 1899-12-31 | 1900-02-28 |
| 1999-12-31 | 1999-11-30 | 2000-01-31 |
| 2000-06-04 | 2000-05-31 | 2000-07-31 |
| 2017-03-16 | 2017-02-28 | 2017-04-30 |
+------------+------------------------+-------------------------+
5 rows in set (0.00 sec)
為了得到某個月份的天數,可以先使用 LAST_DAY()
函數,然後再使用 DAYOFMONTH()
函數:
mysql> SELECT d, DAYOFMONTH(LAST_DAY(d)) AS 'days in month' FROM date_val;
+------------+---------------+
| d | days in month |
+------------+---------------+
| 1864-02-28 | 29 |
| 1900-01-15 | 31 |
| 1999-12-31 | 31 |
| 2000-06-04 | 30 |
| 2017-03-16 | 31 |
+------------+---------------+
5 rows in set (0.00 sec)
5. 查找某日期的星期名
問題
你希望知道某給定日期的星期名。
解决方案
使用 DAYNAME()
函數。
討論
想要知道某給定日期對應的星期名,可以使用 DAYNAME()
函數:
mysql> SELECT CURDATE(), DAYNAME(CURDATE());
+------------+--------------------+
| CURDATE() | DAYNAME(CURDATE()) |
+------------+--------------------+
| 2022-07-03 | Sunday |
+------------+--------------------+
1 row in set (0.00 sec)
DAYNAME()
函數通常會和其他日期相關的操作結合使用。例如,想要確定某月第一天的星期名,可以結合函數 DAYOFMONTH()
:
mysql> SET @d = CURDATE();
Query OK, 0 rows affected (0.00 sec)
mysql> SET @first = DATE_SUB(@d, INTERVAL DAYOFMONTH(@d) - 1 DAY);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @d AS 'starting date',
-> @first AS '1st of month date',
-> DAYNAME(@first) AS '1st of month day';
+---------------+-------------------+------------------+
| starting date | 1st of month date | 1st of month day |
+---------------+-------------------+------------------+
| 2022-07-03 | 2022-07-01 | Friday |
+---------------+-------------------+------------------+
1 row in set (0.00 sec)
6. 查找某一周中任何一個工作日的日期
問題
你需要計算給定日期所在周的某個工作日對應的日期。假設你希望知道和 2014-07-09 在同一周的周二所對應的日期。
解决方案
見下列討論部分。
討論
由於給定了日期 2014-07-09 ,想要知道和該日期在同一周的周二對應的日期,那麼你需要先知道 2014-07-09 這個日期對應的星期數。例如,如果 2014-07-09 對應周一,那麼和該日期在同一周的周二對應的日期就是該日期向後平移一天,即 2014-07-10 ;如果 2014-07-09 對應周三,那麼和該日期在同一周的周二對應的日期就是該日期向前平移一天,即 2014-07-08 。
針對以上需求,MySQL 提供了兩個有用的函數。函數 DAYOFWEEK()
將周日視為一周的第一天,周六視為一周的最後一天,且從周日到周六依次對應數字 1 1 1 到 7 7 7 ;函數 WEEKDAY()
將周一視為一周的第一天,且從周一到周日依次對應數字 0 0 0 到 6 6 6 。
有了上述函數,那麼求解上述問題可以按照這樣的思路,即:
- 首先,將給定日期向前平移
DAYOFWEEK()
天,這一步會得到一個日期,且該日期總是給定日期所在周前一周的周六; - 接著,將第 1 1 1 步中得到的日期向後平移一天則得到周日的日期,向後平移兩天則得到周一的日期,依次類推。
上述步驟對應到 SQL 語句如下:
DATE_ADD(DATE_SUB(d, INTERVAL DAYOFWEEK(d) DAY), INTERVAL n DAY)
其中 d
錶示給定的一個日期, n n n 的取值範圍是從 1 1 1 到 7 7 7,分別可以得到和日期 d
在同一周的周日到周六對應的日期。實際上,上述錶達式還可以進一步簡化如下:
針對 date_val
錶中的記錄,可以得到和其中的日期在同一周的周日和周六(這裏周日為一周的第一天,周六的最後一天)所對應的日期如下:
mysql> SELECT d, DAYNAME(d) AS day,
-> DATE_ADD(d, INTERVAL 1 - DAYOFWEEK(d) DAY) AS Sunday,
-> DATE_ADD(d, INTERVAL 7 - DAYOFWEEK(d) DAY) AS Saturday
-> FROM date_val;
+------------+----------+------------+------------+
| d | day | Sunday | Saturday |
+------------+----------+------------+------------+
| 1864-02-28 | Sunday | 1864-02-28 | 1864-03-05 |
| 1900-01-15 | Monday | 1900-01-14 | 1900-01-20 |
| 1999-12-31 | Friday | 1999-12-26 | 2000-01-01 |
| 2000-06-04 | Sunday | 2000-06-04 | 2000-06-10 |
| 2017-03-16 | Thursday | 2017-03-12 | 2017-03-18 |
+------------+----------+------------+------------+
5 rows in set (0.00 sec)
边栏推荐
猜你喜欢
将函数放在模块中
LB10S-ASEMI整流桥LB10S
一网打尽异步神器CompletableFuture
MySQL - database query - sort query, paging query
Can and can FD
Hundred days to complete the open source task of the domestic database opengauss -- openguass minimalist version 3.0.0 installation tutorial
量价虽降,商业银行结构性存款为何受上市公司所偏爱?
Sorry, we can't open xxxxx Docx, because there is a problem with the content (repackaging problem)
Datapipeline was selected into the 2022 digital intelligence atlas and database development report of China Academy of communications and communications
Although the volume and price fall, why are the structural deposits of commercial banks favored by listed companies?
随机推荐
CAN和CAN FD
Rocky basics 1
无密码身份验证如何保障用户隐私安全?
Get you started with Apache pseudo static configuration
#从源头解决# 自定义头文件在VS上出现“无法打开源文件“XX.h“的问题
mysql econnreset_Nodejs 套接字报错处理 Error: read ECONNRESET
Word document injection (tracking word documents) incomplete
Sorry, we can't open xxxxx Docx, because there is a problem with the content (repackaging problem)
Go pointer
restTemplate详解
Flutter draws animation effects of wave movement, curves and line graphs
leetcode 10. Regular expression matching regular expression matching (difficult)
【每日一题】1200. 最小绝对差
Flutter 绘制波浪移动动画效果,曲线和折线图
事务的基本特性和隔离级别
How to protect user privacy without password authentication?
Notion 类笔记软件如何选择?Notion 、FlowUs 、Wolai 对比评测
DataPipeline双料入选中国信通院2022数智化图谱、数据库发展报告
南理工在线交流群
Rocky基础命令3