向表插入「當前時間」看似簡單,實則暗藏精度差異、時區漂移、格式模型依賴、隱式轉換導致索引失效等坑。本文對比SYSDATE/SYSTIMESTAMP/CURRENT_TIMESTAMP、演示正確插入與查詢模式、給出批量綁定範例、分區表時間欄位裁剪寫法。
| 函數 | 返回類型 | 精度 | 時區 | 適用場景 |
|---|---|---|---|---|
SYSDATE | DATE | 秒 | DB服務器OS時區 | 傳統系統、無毫秒需求、單時區部署 |
SYSTIMESTAMP | TIMESTAMP WITH TIME ZONE | 微秒(6位) | DB服務器OS時區 | 新系統、需高精度、跨時區、審計追蹤 |
CURRENT_TIMESTAMP | TIMESTAMP WITH TIME ZONE | 微秒 | Session時區(SESSIONTIMEZONE) | 應用層控制時區、多租戶不同顯示時區 |
LOCALTIMESTAMP | TIMESTAMP | 微秒 | Session時區(無時區信息) | 本地化顯示、不存儲時區 |
CREATE TABLE event_log (
id NUMBER GENERATED ALWAYS AS IDENTITY,
event_type VARCHAR2(30),
event_ts TIMESTAMP WITH TIME ZONE, -- 推薦:存儲時區感知時間
created_at DATE DEFAULT SYSDATE -- 兼容舊代碼
);
-- 1. 直接插入 (隱式轉換安全,因目標類型明確)
INSERT INTO event_log (event_type, event_ts) VALUES ('LOGIN', SYSTIMESTAMP);
-- 2. 批量綁定 (Java/JDBC PreparedStatement / PL/SQL FORALL)
-- PL/SQL範例:
DECLARE
TYPE ts_arr IS TABLE OF TIMESTAMP WITH TIME ZONE INDEX BY PLS_INTEGER;
v_ts ts_arr;
BEGIN
FOR i IN 1..10000 LOOP
v_ts(i) := SYSTIMESTAMP; -- 或從業務邏輯獲取
END LOOP;
FORALL i IN 1..10000
INSERT INTO event_log (event_type, event_ts) VALUES ('BATCH', v_ts(i));
COMMIT;
END;
-- 目標:查詢 2024-01-15 00:00:00 至 23:59:59.999999 的記錄
-- ❌ 錯誤:BETWEEN 邊界包含問題
WHERE event_ts BETWEEN TO_TIMESTAMP('2024-01-15 00:00:00','YYYY-MM-DD HH24:MI:SS')
AND TO_TIMESTAMP('2024-01-15 23:59:59','YYYY-MM-DD HH24:MI:SS')
-- ✅ 正確:半開區間 [start, next_day_start)
WHERE event_ts >= TO_TIMESTAMP_TZ('2024-01-15 00:00:00+08:00','YYYY-MM-DD HH24:MI:SS TZH:TZM')
AND event_ts < TO_TIMESTAMP_TZ('2024-01-16 00:00:00+08:00','YYYY-MM-DD HH24:MI:SS TZH:TZM')
-- ✅ 正確:TRUNC配合函數索引 (11g+)
WHERE TRUNC(event_ts) = TO_DATE('2024-01-15','YYYY-MM-DD')
-- TO_DATE/TO_TIMESTAMP 對分隔符寬容:只要fmt與string位置對應,分隔符可任意
TO_DATE('2024/01/15 12:00:00', 'YYYY-MM-DD HH24:MI:SS') -- 正常
TO_DATE('2024;01;15 12:00:00', 'YYYY-MM-DD HH24:MI:SS') -- 正常
TO_DATE('2024-01-15 12:00:00', 'YYYY/MM/DD HH24:MI:SS') -- 正常
-- 原因:Oracle解析器按fmt中格式元素順序提取數字,非格式元素字符直接跳過匹配
-- 範圍分區表 (按天分區)
CREATE TABLE sales_part (
sale_id NUMBER,
sale_dt TIMESTAMP WITH TIME ZONE,
amount NUMBER
)
PARTITION BY RANGE (sale_dt) INTERVAL (NUMTODSINTERVAL(1,'DAY'))
( PARTITION p_init VALUES LESS THAN (TO_TIMESTAMP_TZ('2024-01-01+08:00','YYYY-MM-DD TZH:TZM')) );
-- 查詢 2024-01-15 分區裁剪生效
SELECT * FROM sales_part
WHERE sale_dt >= TO_TIMESTAMP_TZ('2024-01-15+08:00','YYYY-MM-DD TZH:TZM')
AND sale_dt < TO_TIMESTAMP_TZ('2024-01-16+08:00','YYYY-MM-DD TZH:TZM');
DBTIMEZONE (查詢SELECT DBTIMEZONE FROM dual)oracle.jdbc.timezoneAsRegion=false (避免JDBC驅動時區轉換Bug)TIMESTAMP WITH TIME ZONE,顯示層按用戶SESSIONTIMEZONE轉換。DBTIMEZONE保持一致,或統一UTC。時間戳處鐵律:存儲帶時區、查詢半開區間、插入用字面量/綁定變數、分區裁剪友好。新系統建議全面遷移至TIMESTAMP WITH TIME ZONE + SYSTIMESTAMP,徹底解決時區漂移與精度不足。