在資料庫效能調優中,時間資訊是核心判斷依據。早期 Oracle 採用厘秒(Centisecond,1/100 秒)作為時間單位,導致微秒級操作要么記錄為 0(起止在同一厘秒),要么誤記為 1 厘秒(跨厘秒邊界),嚴重失真。隨著 SQL:1999 標準引入,Oracle 9i/10g 以降提供 INTERVAL、TIMESTAMP 等高精度時間類型,徹底解決精度與計算難題。本文系統梳理時間類型演進、核心函數、實戰查詢模式與效能視圖應用。
| 類型 | 精度 | 特性 | 適用場景 |
|---|---|---|---|
DATE | 秒 | 儲存時間點,含年月日時分秒,無時區、無小數秒 | 傳統業務時間戳(訂單時間、出生日期) |
TIMESTAMP [(fractional_seconds_precision)] | 最高 9 位小數秒 (納秒) | 時間點,精度可配置 (預設 6 位 = 微秒),無時區 | 高精度審計、日誌記錄、效能埋點 |
TIMESTAMP WITH TIME ZONE | 納秒 | 含時區位移 (如 +08:00),跨地域系統自動換算 | 全球化應用、多資料中心同步 |
TIMESTAMP WITH LOCAL TIME ZONE | 納秒 | 儲存時正規化為資料庫時區,讀取時轉換為會話時區 | 跨時區用戶見「本地時間」的應用 |
INTERVAL YEAR [(year_precision)] TO MONTH | 年-月 | 年月間隔,支援年精度 (預設 2 位) | 年齡計算、合同年限、歸檔保留期 |
INTERVAL DAY [(day_precision)] TO SECOND [(frac_sec_prec)] | 納秒 | 日時分秒間隔,完整支援小數秒 | 效能持續時間、ETL 耗時、SLA 計算 |
TO_DATE / TO_TIMESTAMP / TO_CHAR-- DATE (傳統)
TO_DATE('2024-12-25 14:30:45', 'YYYY-MM-DD HH24:MI:SS')
-- TIMESTAMP (高精度)
TO_TIMESTAMP('2024-12-25 14:30:45.123456', 'YYYY-MM-DD HH24:MI:SS.FF6')
-- 格式化輸出
TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3 TZR') -- 含毫秒與時區
TO_CHAR(CURRENT_TIMESTAMP(5), 'DD-MON-YYYY HH24:MI:SSxFF') -- xFF 顯示小數秒
INTERVAL '2-6' YEAR TO MONTH -- 2 年 6 月
INTERVAL '10 12:30:45.123' DAY TO SECOND -- 10 天 12 時 30 分 45.123 秒
-- 注意:YEAR TO MONTH 用「-」分隔,DAY TO SECOND 用空格分隔
-- 日期/時間點 ± 間隔 = 日期/時間點
SYSDATE + INTERVAL '30' DAY
SYSTIMESTAMP - INTERVAL '2' HOUR
-- 日期/時間點 - 日期/時間點 = INTERVAL DAY TO SECOND
SYSTIMESTAMP - (SYSTIMESTAMP - INTERVAL '1' DAY) -- 產生 INTERVAL '1 0:0:0.0' DAY TO SECOND
-- 間隔 ± 間隔 = 間隔
INTERVAL '1' DAY + INTERVAL '12' HOUR -- INTERVAL '1 12:0:0' DAY TO SECOND
-- 當前高精度時間點
CURRENT_TIMESTAMP(6) -- 會話時區,納秒
SYSTIMESTAMP -- 資料庫時區,納秒
LOCALTIMESTAMP(6) -- 會話時區,無時區資訊
-- 截取/截斷
TRUNC(SYSTIMESTAMP, 'MI') -- 截至分鐘
ROUND(SYSTIMESTAMP, 'HH') -- 四捨五入至小時
-- 邊界計算
LAST_DAY(SYSDATE) -- 本月最後一天
ADD_MONTHS(SYSDATE, -12) -- 12 個月前
NEXT_DAY(SYSDATE, 'FRIDAY')-- 下一個周五
-- 間隔提取
EXTRACT(SECOND FROM INTERVAL '1 12:30:45.123' DAY TO SECOND) -- 45.123
SELECT sql_id,
elapsed_time/1e6 elapsed_sec, -- 微秒轉秒
cpu_time/1e6 cpu_sec,
(elapsed_time - cpu_time)/1e6 wait_sec, -- 等待時間 = 總耗時 - CPU 時間
executions,
(elapsed_time/NULLIF(executions,0))/1e6 avg_sec
FROM v$sql
WHERE elapsed_time > 1e6 -- 超過 1 秒的 SQL
ORDER BY elapsed_time DESC;
陷阱:V$SQL 時間單位為微秒 (10-6 秒),非毫秒,除以 1e6 轉秒。
SELECT event, total_waits,
time_waited_micro/1e6 total_wait_sec,
average_wait/1e6 avg_wait_sec
FROM v$system_event
WHERE wait_class != 'Idle'
ORDER BY time_waited_micro DESC;
局限:僅見資料庫內部等待,不含 OS 層等待(CPU 排隊、磁碟控制器佇列、網絡傳輸)。
SELECT s.sid, s.username, sw.event, sw.wait_time_micro/1e6 wait_sec,
sw.seconds_in_wait, sw.state
FROM v$session_wait sw
JOIN v$session s ON sw.sid = s.sid
WHERE sw.wait_class != 'Idle'
ORDER BY sw.wait_time_micro DESC;
需求:判斷某用戶對某會議記錄的最新記錄時間是否在 14 個月內。
SELECT COUNT(*) AS contains
FROM dual
WHERE CURRENT_DATE < (
SELECT ADD_MONTHS(MAX(med_time), 14)
FROM red_meettingrecord
WHERE med_tar_id = '225' AND med_usr_id = '001387'
);
CURRENT_DATE:會話時區當前日期 (無時間分量)MAX(med_time):假設 med_time 為 DATE 或 TIMESTAMPADD_MONTHS(..., 14):向前推 14 個月,自動處理月份天數差異TIMESTAMP(6):微秒精度滿足 99% 場景,避免 DATE 秒級精度不足TIMESTAMP WITH LOCAL TIME ZONE:消除應用層時區轉換 BugINTERVAL:可讀性、精度、效能三贏,拒絕手工換算秒數V$SQL、V$SYSTEM_EVENT、V$SESSION_WAIT 均為微秒,報表層統一除以 1e6 顯示秒DBMS_UTILITY.GET_TIME:厘秒精度已淘汰,改用 DBMS_UTILITY.GET_CPU_TIME (微秒) 或 SYSTIMESTAMP 差值從厘秒到納秒,從手工換算到原生 INTERVAL 運算,Oracle 時間處理能力的進化,映照著資料庫從「記錄業務」向「精準洞察」的轉型。掌握高精度時間類型、原生間隔運算、效能視圖時間解讀,是現代 DBA 與開發者從「會用時間」進階為「駕馭時間」的必經之路。下次寫時間邏輯時,請問自己:「我用的是 DATE 還是 TIMESTAMP?間隔算的是秒數還是 INTERVAL?」——答案決定了你的系統精度上限。