首頁 > 軟體

MySQL之DATETIME與TIMESTAMP的時間精度問題

2023-02-25 06:04:10

datetime與timestamp時間精度問題

  • 預設時間精度與最大時間精度
  • 更改資料庫中所有指定欄位的型別的儲存過程(用於修正時間精度)

預設時間精度與最大時間精度

-- 建立資料庫
CREATE DATABASE mydb_1;

-- 檢視建立資料庫建表語句(預設編碼UTF8)
SHOW CREATE DATABASE mydb_1;

-- 建立表
-- 測試datetime的精度
CREATE TABLE test(
	-- 預設精度為0
	-- Maximum is 6.
	datetime1 DATETIME,
	datetime2 DATETIME(3),
	datetime3 DATETIME(5)
);
INSERT INTO test VALUES("2020-11-22 12:01:01.59999", "2020-11-22 12:01:01.59999", "2020-11-22 12:01:01.59999");
INSERT INTO test VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.599", "2020-11-22 12:01:01.59999");
INSERT INTO test VALUES("2020-11-22 12:01:01.4", "2020-11-22 12:01:01.594", "2020-11-22 12:01:01.59994");
INSERT INTO test VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.5995", "2020-11-22 12:01:01.599995");
INSERT INTO test VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.5994", "2020-11-22 12:01:01.599994");


-- 建立表
-- 測試timestamp的精度
CREATE TABLE test11(
	-- 預設精度為0
	-- Maximum is 6.
	datetime1 TIMESTAMP,
	datetime2 TIMESTAMP(3),
	datetime3 TIMESTAMP(5)
);
INSERT INTO test1 VALUES("2020-11-22 12:01:01.59999", "2020-11-22 12:01:01.59999", "2020-11-22 12:01:01.59999");
INSERT INTO test1 VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.599", "2020-11-22 12:01:01.59999");
INSERT INTO test1 VALUES("2020-11-22 12:01:01.4", "2020-11-22 12:01:01.594", "2020-11-22 12:01:01.59994");
INSERT INTO test1 VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.5995", "2020-11-22 12:01:01.599995");
INSERT INTO test1 VALUES("2020-11-22 12:01:01.5", "2020-11-22 12:01:01.5994", "2020-11-22 12:01:01.599994");

更改資料庫中所有指定欄位的型別的儲存過程(用於修正時間精度)

-- 結束符修改為$$
DELIMITER $$
DROP PROCEDURE IF	EXISTS batch_alter_column_type $$
CREATE PROCEDURE batch_alter_column_type ( 
	sch_name VARCHAR ( 128 ), -- 庫名稱
	from_col_name VARCHAR ( 32 ), -- 修改的欄位
	to_col_type VARCHAR ( 32 ) -- 修改之後的欄位型別
) 
BEGIN
  -- 當前表名
	DECLARE tbl_name VARCHAR ( 64 );
  -- 當前欄位名
	DECLARE col_name VARCHAR ( 64 );
	-- 遊標結束標記
	DECLARE i_done INT ( 1 );
	-- 修改的SQL語句
	DECLARE SQL_FOR_ALTER VARCHAR ( 1024 );
	-- 宣告遊標,儲存要修改表和欄位
	DECLARE mycursor CURSOR FOR 
	   SELECT C.TABLE_NAME, C.COLUMN_NAME 
		 FROM INFORMATION_SCHEMA.COLUMNS C
		 LEFT JOIN INFORMATION_SCHEMA.TABLES T
		 ON C.TABLE_NAME = T.TABLE_NAME
		 AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
	   WHERE C.TABLE_SCHEMA = sch_name  -- 字串型別轉換
	   AND TABLE_TYPE   = 'BASE TABLE'	 
		 AND COLUMN_NAME  = from_col_name;
	-- 遊標中的內容執行完後將標記設定為1  
	DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_done = 1;
  -- 開啟遊標	
	OPEN mycursor;
  -- 執行迴圈	
	Lp:LOOP	
		-- 取出遊標中的值
		FETCH mycursor INTO tbl_name,col_name;
	  -- 如果標記為1,退出迴圈
		IF i_done = 1 THEN 
			 LEAVE Lp;
		END IF;
		-- 構造修改語句
		SET SQL_FOR_ALTER = CONCAT( "ALTER TABLE ", tbl_name, " MODIFY COLUMN ", col_name, " ", to_col_type );
		-- 給區域性變數賦值
		SET @SQL = SQL_FOR_ALTER;
		-- 預處理SQL語句
		PREPARE stmt FROM @SQL;
		-- 執行SQL語句
		EXECUTE stmt;
	END LOOP;
	-- 釋放遊標
	CLOSE mycursor;
END
$$
-- 呼叫儲存過程
DELIMITER ;
call batch_alter_column_type('mydb_1','MODISTAMP', 'datetime(3)');

使用ALTER修改表的欄位

  • CHANGE:可修改表列名稱和屬性
  • MODIGY:只可修改表列的屬性
-- 修改test表中datetime1欄位屬性為DATETIME(3)
ALTER TABLE test MODIFY COLUMN datetime1 DATETIME(3);

-- 修改test表中datetime1欄位名稱為datetime11,屬性為DATETIME(2)
ALTER TABLE test CHANGE datetime1 datetime11 DATETIME(2);

MySQL中選datetime還是timestamp呢?

1. 基本區別

型別所佔位元組格式範圍
TIMESTAMP4位元組YYYY-MM-DD HH:MM:SS1970-01-01 00:00:01utc到2038-01-19 03:14:07utc
DATETIME5位元組YYYY-MM-DD HH:MM:SS1000-01-01 00:00:00到9999-12-31 23:59:59
DATE3位元組YYYY-MM-DD1000-01-01到9999-12-31
TIME3位元組HH:MM:SS-838:59:59到838:59:59
YEAR1位元組YYYY1901到2155

注:MySQL 5.6.4 之前,佔 8 個位元組 ,之後版本,佔 5 個位元組。

2. 其他特性

1. TIMESTAMP是以utc格式儲存,會自動檢索當前時區對時間進行轉換,而DATETIME不會。

2. 存入null時,TIMESTAMP會自動儲存當前時間,而DATETIME儲存null值。

3. 時間計算:

DATETIME翻譯為漢語即"時間戳",它是當前時間到 Unix元年(1970 年 1 月 1 日 0 時 0 分 0 秒)的秒數。對於某些時間的計算,如果是以 DATETIME 的形式會比較困難,假如我是 1994-1-20 06:06:06 出生,現在的時間是 2016-10-1 20:04:50 ,那麼要計算我活了多少秒鐘, DATETIME還需要函數進行轉換,但是 TIMESTAMP 直接相減就行。

3. 什麼場景下用什麼型別合適呢?

1.需要跨時區計算時間用 或者 需要自動更新時間的TIMESTAMP

計算一架從北京飛往紐約的飛機的飛行時間。這個場景中,如果使用 TIMESTAMP 來存時間,起飛和降落時間的值,都會被轉換成 UTC 時間,所以它們直接相減即可獲得結果。但如果使用 DATATIME 格式存時間,還需要進行轉換,才可以完成,容易出錯。

2.記錄建立修改時間 或者 時間範圍大於2038 用DATETIME

DATATIME作為記錄時間,現在都已經2022年了,很快就到2038年啦,使用DATATIME不需要擔心超過範圍。

當然在兩者都滿足使用的情況下,所佔位元組越小越好,TIMESTAMP比DATATIME好。

4.BIGINT使用(佔8位元組)

還有一種情況,即不用TIMESTAMP也不用DATATIME,而是用BIGINT。儲存自紀元以來的毫秒數(如果使用的是 Java,則用 System.currentTimeMillis() 獲取當前時間)

這樣有幾個優點:

1. 可以在遷移資料庫時避免因為資料型別差異。比如MySQL的DATETIME型別和Oracle的DATETIME型別之間可能存在差異,timestamp型別的精度可能也存在差異,MySQL的timestamp精度不是一開始就支援毫秒精度的。

2. 沒有時區問題。無論是哪個時區,因為開始計算的時間不同,無論當前時間如何,跨度是一致的。也沒有timestamp和datatime的範圍問題。是對timestamp的補充。

3. InnoDB儲存引擎下,通過時間範圍查詢,效能bigint > datetime > timestamp,通過時間排序,效能bigint > timestamp > datetime。綜合來講,bigint效能最好。

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com