1. 程式人生 > >【小家SQL】MySql資料型別---日期時間型別的使用(含datetime和timestamp的區別)

【小家SQL】MySql資料型別---日期時間型別的使用(含datetime和timestamp的區別)

每篇一句

練武不練功,到老一場空。

程式設計師應該注重內功的修煉,那才是核心競爭力

說在前面

在這一路學習過來,每次不管看書還是網上看的資料,對於MySQL資料型別中的時間日期型別總是一掃而過,不曾停下來認認真真的研究學習。最近看了一本關於MySql的書籍,打算全面的學習研究一遍。

雖然每次都沒有怎麼注意,但是使用起來確實非常的糟糕。每次都轉換起來非常不便。比如明明我只需要日期,卻給我一個時間戳或者時分秒都有的東西之類的。

在之前,我對於時間日期資料型別不怎麼感冒,也沒怎麼用過這一型別。在我的做專案裡用到存貯時間的資料,我都是採用int整型資料型別來儲存,即是儲存時間戳。但是在後面學習MySQL優化的時候,就有一個原則就是儲存資料時應採用最小佔用空間的資料型別。int型別是4個位元組,TIMESTAMP也是4個位元組,但是在需要使用日期時,時間戳還需要進一步轉換,而TIMESTAMP型別資料就不需要了。

時間日期資料型別總概況

MySQL中有多種表示時間日期的資料型別,主要有YEAR、TIME、DATE、DATETIME、TIMESTAMP等。每一種資料型別都有儲存的時間日期格式、以及取值範圍,因此在使用時間日期資料型別的時候需要選取最佳的資料型別。
在這裡插入圖片描述

此處注意發現:Time型別HH的取值竟然是800+,而不是我們本以為的24以內的數字,具體原因,下面會有解釋

各型別詳細講解

1、Year

見名之意,year用於儲存年,儲存時只需要一個位元組,插入資料時可以使用各種格式指定YEAR值(非常節約記憶體,所以當你只需要年的時候,用此欄位合適)。

支援的常見插入格式為:

  1. (推薦甚至強制要求必須)四位字串
    或者數字,範圍為“1901”~“2155”,寫多少即為多少
  2. 兩位字串格式:範圍為“00”“99”,"00""69"被轉化為20**(例如:“16”轉化為“2016”),“70”~“99”被轉化為19**(下同)
  3. 兩位數字格式,範圍為199,169被轉化為20**(例如:1轉化為2001),70~99被轉化為19**
2、Time

time用於儲存時間資訊,儲存時需要三個位元組。
雖然,小時的範圍是0~~23,但是為了表示某種特殊需要的時間間隔,將Time型別的範圍擴大了。而且還支援了負值。

支援的常見插入格式為:

  1. (推薦甚至強制要求必須)字串格式:‘HH:MM:SS’,‘HH:MM’,‘D HH:MM’,‘D HH’,'SS’等形式。舉個例子,輸入‘30’,Time型別會自動轉換為00:00:30。
  2. ‘HHMMSS’格式的字串或HHMMSS格式的數值表示,例如,輸入‘123456’,Time型別會轉換成12:34:56;輸入123456,Time型別會轉換成12:34:56。如果輸入0或者‘0’,那麼TIME型別會轉換為0000:00:00。
  3. ‘D HH:MM:SS’格式的字串表示。其中,D表示天數,取值範圍是0~~34。儲存時,小時的值等於(D*24+HH)。舉個例子,輸入‘2 11:30:50’,Time型別會轉換為59:30:50。
  4. 使用current_time或者current_time()或者now()輸入當前系統時間。(一般用於預設值)

SQL示例:

INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('1', '1 01:50:50');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('2', '01:50:50');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('3', '50:05');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('4', '1 05:05');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('5', '59');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('6', '66');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('7', '123456');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('8', 123456);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('9', 0);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('10', '0');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('11', now());
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('12', current_time);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('13', current_time());

Time型別專門用來儲存時間資料,而且只佔3個位元組,所以如果只需要記錄時間,選擇Time型別是最合適的。

3、Date

date用於儲存日期,沒有時間部分,儲存時需要三個位元組。

MySQL中是以YYYY-MM-DD的形式顯示date型別的值。

支援的常見插入格式為:

  • (推薦甚至強制要求必須) ‘YYYY-MM-DD’或‘YYYYMMDD’格式的字串表示,這種方式可以表達的範圍是‘1000-01-01’~‘9999-12-31’。
  • MySQL中還支援一些不嚴格的語法格式,任何標點都可以用來做間隔符。如’YYYY/MM/DD‘,’[email protected]@DD‘,’YYYY.MM.DD‘等分隔形式。舉個例子,輸入’2011.3.8‘,date型別將轉換為2011-03-08。
  • ’YY-MM-DD‘或者’YYMMDD‘格式的字串表示,其中’YY‘的取值,’00‘’69‘轉換為20002069,’70‘’99‘轉換為19701999。與year型別類似。
  • 使用current_date或now()來輸入當前系統時間。

SQL示例:

INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('1', '2008-08-08');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('2', '20080808');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('3', '[email protected]@08');
-- 格式出錯,所以插入0000-00-00
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('4', '2008#0808');
-- 格式沒錯,但是小於了date型別的最小值1000,但是資料庫還是插進去了,我暈
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('5', '0999-08-08');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('6', '690808');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('7', '700808');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('8', 690808);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('9', 700808);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('10', '0');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('11', 0);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('12', now());
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('13', current_date);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('14', current_date());
4、Datetime

datetime型別使用8個位元組來表示日期和時間。
MySQL中以‘YYYY-MM-DD HH:MM:SS’的形式來顯示dateTime型別的值。

支援的常見插入格式為:

  • (推薦甚至強制要求必須)‘YYYY-MM-DD HH:MM:SS’或‘YYYYMMDDHHMMSS’格式的字串表示。這種方式可以表達的範圍是‘1000-01-01 00:00:00’~~‘9999-12-31 23:59:59’。
  • MySQL中還支援一些不嚴格的語法格式,任何的標點都可以用來做間隔符。情況與date型別相同,而且時間部分也可以使用任意的分隔符隔開,這與Time型別不同,Time型別只能用‘:’隔開呢。
  • 使用now()來輸入當前系統日期和時間。

SQL示例:

INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('1', '2008-08-08 08:08:08');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('2', '20080808080808');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('3', '[email protected]@08 08*08*08');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('4', '69-01-01 11:11:11');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('5', '70-01-01 11:11:11');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('6', 20080808080808);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('7', 690808080808);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('8', 700808080808);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('9', 0);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('10', now());

dateTime型別用來記錄日期和時間,其作用等價於date型別和Time型別的組合。一個dateTime型別的欄位可以用一個date型別的欄位和一個time型別的欄位代替。但是如果需要同時記錄日期和時間,選擇dateTime型別是個不錯的選擇。

5、timestamp

timestamp型別使用4個位元組來表示日期和時間。
timestamp型別與dateTime型別顯示的格式是一樣的。

支援的常見插入格式為:
同datetime

二者主要區別在於取值範圍。

  • timestamp儲存需要四個位元組,它的取值範圍為“1970-01-01 00:00:01” UTC ~ “2038-01-19 03:14:07” (和時區有關
  • 而datetime取值範圍為“1000-01-01 00:00:00” ~ “9999-12-31 23:59:59”(和時區無關,怎麼存入怎麼返回,對程式設計師友好

SQL示例:同datetime(但使用的是current_timestamp和now())

INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('1', null);
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('2', 'NULL');
INSERT INTO `linkinframe`.`test` (`id`, `a`) VALUES ('3', current_timestamp());

從資料庫顯示的結果來看,timestamp的範圍確實很小的,2069明顯的超過了2038,所以資料庫插入0。

在MySQL 5.6.5版本之前,Automatic Initialization and Updating只適用於TIMESTAMP,而且一張表中,最多允許一個TIMESTAMP欄位採用該特性。從MySQL 5.6.5開始,Automatic Initialization and Updating同時適用於TIMESTAMP和DATETIME,且不限制數量。

datetime和timestamp的比較

1、timestamp相對於datetime的不同之處:
(1.1),使用current_timestamp來輸入系統當前日期與時間
(1.2),輸入null時,系統會輸入系統當前日期與時間
(1.3),無任何輸入時,系統會輸入null。

資料上面說系統會輸入系統當前日期與時間,但是我自己嘗試了下,如果輸入null的時候,資料庫中也是null,鬱悶。 估摸和MySql版本有關

2、timestamp型別還有一個很大的特殊點,就是時間是根據時區來顯示的。
例如,在東八區插入的timestamp型別為2009-09-30 14:21:25,在東七區顯示時,時間部門就變成了13:21:25,在東九區顯示時,時間部門就變成了15:21:25。
3、需要顯示日期與時間,timestamp型別需要根據不同地區的時區來轉換時間,但是,timestamp型別的範圍太小,其最大時間為2038-01-19 11:14:07。
如果插入時間的比這個大,將會資料庫插入0000-00-00 00:00:00。所以需要的時間範圍比較大,還是選擇dateTime型別比較安全。

MySQL中如何表示當前時間?

其實,表達方式還是蠻多的,彙總如下:
CURRENT_TIMESTAMP
CURRENT_TIMESTAMP()
NOW()
LOCALTIME
LOCALTIME()
LOCALTIMESTAMP
LOCALTIMESTAMP()

小結

瞭解MySQL的日期時間資料型別對於選取一種適合儲存型別是很有必要的。假若只有儲存年份可以選取YEAR、僅儲存時間可以選擇TIME、又或者需要儲存完整日期時間,那麼可以根據實際情況選取DATATIME(推薦)或者TIMESTAMP資料型別。

附:MySql常用轉換函式

unix_timestamp(),
unix_timestamp(date),
from_unixtime(unix_timestamp),
from_unixtime(unix_timestamp,format)

select unix_timestamp(); -- 1218290027
select unix_timestamp('2008-08-08'); -- 1218124800
select unix_timestamp('2008-08-08 12:30:00'); -- 1218169800

select from_unixtime(1218290027); -- '2008-08-09 21:53:47'
select from_unixtime(1218124800); -- '2008-08-08 00:00:00'
select from_unixtime(1218169800); -- '2008-08-08 12:30:00'

select from_unixtime(1218169800, '%Y %D %M %h:%i:%s %x'); -- '2008 8th August 12:30:00 2008'

date_format(date,format), time_format(time,format) 能夠把一個日期/時間轉換成各種各樣的字串格式。它是 str_to_date(str,format) 函式的 一個逆轉換。

附:MySql各大資料型別佔用位元組數

在這裡插入圖片描述
在這裡插入圖片描述

修正:varchar最大大小是65532位元組。char是定長(每個值都佔用M個位元組,如果某個長度小於M,MySQL就會在它的右邊用空格字元補足),varchar是變長

在這裡插入圖片描述
在這裡插入圖片描述

附:mysql中的date型別直接比較大小是按照字串比較還是時間戳

Mysql在比較兩種不同資料型別時,第一步是將他們轉化為同一種類型,然後在比較。那麼Date和String在比較的時候,一定是把String轉化為Date嗎?答案是Yes.

例子:

select * FROM test.orders where ceate_record_time > '2019'

結果截圖:
在這裡插入圖片描述
為什麼會出現 2018 的字串?

再舉個例子:如果是用字串比較,“2004-04-31"這個string應該比2004-01-01這個date來得大,但是4-31是一個invalid的日期(4月是小月),會被轉化成"0000-00-00”,所以2004-01-01 (日期) > “2014-04-31”。

所以,在儲存方面:如果你是表示的時間,請儘量不要採用str型別來儲存(雖然大多數情況下存入的效果一樣,但不建議)。
在查詢方面:如果你確實遇到的儲存的是字串,那麼請用STR_TO_DATE函式轉成日期格式在查詢,形如:

select * from orders where date(str_to_date(`ceate_record_time`.`publish_date`,'%Y-%m-%d')) > '2019-0-0'

附:unsigned解釋

整型的每一種都分有無符號(unsigned)和有符號(signed)兩種型別(float和double總是帶符號的),在預設情況下宣告的整型變數都是有符號的型別。

如果需宣告無符號型別的話就需要在型別前加上unsigned。無符號版本和有符號版本的區別就是無符號型別能儲存2倍於有符號型別的正整數資料,比如16位系統中一個int能儲存的資料的範圍為-3276832767,而unsigned能儲存的資料範圍則是065535。由於在計算機中,整數是以補碼形式存放的。根據最高位的不同,如果是1,有符號數的話就是負數;如果是無符號數,則都解釋為正數。

簡而言之就是由於Int型佔4位元組,也就是16位,2^16 = 65535,如果有符號位就+ -兩邊均分,如果沒有就全給+

因此下面建表語句是被推薦的:

CREATE TABLE `fast_group_task` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  ...
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='批量組建班級的任務';

知識交流

在這裡插入圖片描述

若群二維碼失效,請加微訊號(或者掃描下方二維碼):fsx641385712。
並且備註:“java入群” 字樣,會手動邀請入群

在這裡插入圖片描述