如何校驗 MySQL&Oracle 時間字段合規性?

作者:餘振興

愛可生 DBA 團隊成員,熱衷技術分享、編寫技術文檔。

本文來源:原創投稿

  • 愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。

背景信息

在數據遷移或者數據庫低版本升級到高版本過程中,經常會遇到一些由於低版本數據庫參數設置過於寬鬆,導致插入的時間數據不符合規範的情況而觸發報錯,每次報錯再發現處理起來較爲麻煩,是否有提前發現這類不規範數據的方法,以下基於 Oracle 和 MySQL 各提供一種可行性方案作爲參考。

Oracle 時間數據校驗方法

2.1 創建測試表並插⼊測試數據

CREATE TABLE T1(ID NUMBER,CREATE_DATE VARCHAR2(20));

INSERT INTO T1 SELECT 1, '2007-01-01' FROM DUAL;
INSERT INTO T1 SELECT 2, '2007-99-01' FROM DUAL;            -- 異常數據
INSERT INTO T1 SELECT 3, '2007-12-31' FROM DUAL;
INSERT INTO T1 SELECT 4, '2007-12-99' FROM DUAL;            -- 異常數據
INSERT INTO T1 SELECT 5, '2005-12-29 03:-1:119' FROM DUAL;  -- 異常數據
INSERT INTO T1 SELECT 6, '2015-12-29 00:-1:49' FROM DUAL;   -- 異常數據

2.2 創建對該表的錯誤日誌記錄

  • Oracle 可以調用 DBMS_ERRLOG.CREATE_ERROR_LOG 包對 SQL 的錯誤進行記錄,用來記錄下異常數據的情況,十分好用。

  • 參數含義如下

    • T1 爲表名
    • T1_ERROR 爲對該表操作的錯誤記錄臨時表
    • DEMO 爲該表的所屬用戶
EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('T1','T1_ERROR','DEMO');

2.3 創建並插入數據到臨時表,驗證時間數據有效性

-- 創建臨時表做數據校驗
CREATE TABLE T1_TMP(ID NUMBER,CREATE_DATE DATE);

-- 插入數據到臨時表驗證時間數據有效性(增加LOG ERRORS將錯誤信息輸出到錯誤日誌表)
INSERT INTO T1_TMP 
SELECT ID, TO_DATE(CREATE_DATE, 'YYYY-MM-DD HH24:MI:SS')
FROM T1 
LOG ERRORS INTO T1_ERROR REJECT LIMIT UNLIMITED;

2.4 校驗錯誤記錄

SELECT * FROM DEMO.T1_ERROR;

image-20221215162247472

其中 ID 列爲該表的主鍵,可用來快速定位異常數據行。

MySQL 數據庫的方法

3.1 創建測試表模擬低版本不規範數據

-- 創建測試表
SQL> CREATE TABLE T_ORDER(
    ID BIGINT AUTO_INCREMENT PRIMARY KEY,
    ORDER_NAME VARCHAR(64),
    ORDER_TIME DATETIME);

-- 設置不嚴謹的SQL_MODE允許插入不規範的時間數據
SQL> SET SQL_MODE='STRICT_TRANS_TABLES,ALLOW_INVALID_DATES';

SQL> INSERT INTO T_ORDER(ORDER_NAME,ORDER_TIME) VALUES 
    	('MySQL','2022-01-01'),
    	('Oracle','2022-02-30'),
    	('Redis','9999-00-04'),
    	('MongoDB','0000-03-00');

-- 數據示例
SQL> SELECT * FROM T_ORDER;
+----+------------+---------------------+
| ID | ORDER_NAME | ORDER_TIME          |
+----+------------+---------------------+
|  1 | MySQL      | 2022-01-01 00:00:00 |
|  2 | Oracle     | 2022-02-30 00:00:00 |
|  3 | Redis      | 9999-00-04 00:00:00 |
|  4 | MongoDB    | 0000-03-00 00:00:00 |
+----+------------+---------------------+

3.2 創建臨時表進行數據規範性驗證

-- 創建臨時表,只包含主鍵ID和需要校驗的時間字段
SQL> CREATE TABLE T_ORDER_CHECK(
    ID BIGINT AUTO_INCREMENT PRIMARY KEY,
    ORDER_TIME DATETIME);
  
-- 設置SQL_MODE爲5.7或8.0高版本默認值
SQL> SET SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

-- 使用INSERT IGNORE語法插入數據到臨時CHECK表,忽略插入過程中的錯誤
SQL> INSERT IGNORE INTO T_ORDER_CHECK(ID,ORDER_TIME) SELECT ID,ORDER_TIME FROM T_ORDER;

3.3 數據比對

將臨時表與正式表做關聯查詢,比對出不一致的數據即可。

SQL> SELECT 
	T.ID,
	T.ORDER_TIME AS ORDER_TIME,
	TC.ORDER_TIME AS ORDER_TIME_TMP
FROM T_ORDER T INNER JOIN T_ORDER_CHECK TC 
ON T.ID=TC.ID
WHERE T.ORDER_TIME<>TC.ORDER_TIME;

+----+---------------------+---------------------+
| ID | ORDER_TIME          | ORDER_TIME_TMP      |
+----+---------------------+---------------------+
|  2 | 2022-02-30 00:00:00 | 0000-00-00 00:00:00 |
|  3 | 9999-00-04 00:00:00 | 0000-00-00 00:00:00 |
|  4 | 0000-03-00 00:00:00 | 0000-00-00 00:00:00 |
+----+---------------------+---------------------+

一個取巧的小方法

對時間字段用正則表達式匹配,對有嚴謹性要求的情況還是得用以上方式,正則匹配燒腦。

-- Oracle 數據庫
SELECT * FROM  T1 WHERE NOT REGEXP_LIKE(CREATE_DATE,'^((?:19|20)\d\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$');

	ID CREATE_DATE
---------- --------------------
	 2 2007-99-01
	 4 2007-12-99
	 5 2005-12-29 03:-1:119
	 6 2015-12-29 00:-1:49
	 
-- MySQL 數據庫
-- 略,匹配規則還在調試中

關於 SQLE

愛可生開源社區的 SQLE 是一款面向數據庫使用者和管理者,支持多場景審覈,支持標準化上線流程,原生支持 MySQL 審覈且數據庫類型可擴展的 SQL 審覈工具。

SQLE 獲取

類型 地址
版本庫 https://github.com/actiontech/sqle
文檔 https://actiontech.github.io/sqle-docs/
發佈信息 https://github.com/actiontech/sqle/releases
數據審覈插件開發文檔 https://actiontech.github.io/sqle-docs-cn/3.modules/3.7_auditplugin/auditplugin_development.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章