近期專案頻繁涉及時間處理,特此整理筆記,記錄兩個核心時間函數 TRUNC 與 MONTHS_BETWEEN 的用法與避坑指南,希望能幫助到有類似需求的開發者。
假設當前時間 SYSDATE 為 2015/1/30 14:16:03,需求是只保留年月日(即 2015/1/30),且資料型別維持 DATE 不變。
直覺做法是 TO_DATE(TO_CHAR(sysdate,'yyyymmdd'),'yyyymmdd'),但這樣繞轉顯得冗餘。經查證,TRUNC(sysdate) 即可一步到位:
SELECT sysdate FROM dual; -- SYSDATE: 2015/1/30 14:16:03 SELECT TO_DATE(TO_CHAR(sysdate,'yyyymmdd'),'yyyymmdd') FROM dual; -- 結果: 2015/1/30 (DATE 型別) SELECT TRUNC(sysdate) FROM dual; -- 結果: 2015/1/30 (DATE 型別,時分秒歸零)
結論: TRUNC(date) 無需格式參數,直接截去時分秒,回傳當天零點的 DATE 值,效能與可讀性雙贏。
業務規則:每季第一天(如 7/1)需修改上季度 10 天前(含當日,即 4/10 及之前)新增的資料。
若直接用 MONTHS_BETWEEN 比對含時分秒的時間戳,會因「一日之差」導致月份差值微幅波動,造成邊界判斷錯誤:
-- 含時間的比對結果差異
SELECT MONTHS_BETWEEN(TO_DATE('20150701','yyyymmdd'), TO_DATE('20150410 23:59:59','yyyymmdd HH24:mi:ss')) FROM dual; -- 2.67741972
SELECT MONTHS_BETWEEN(TO_DATE('20150701','yyyymmdd'), TO_DATE('20150411 00:00:00','yyyymmdd HH24:mi:ss')) FROM dual; -- 2.67741935
-- 僅比對年月日(去除時分秒)結果穩定
SELECT MONTHS_BETWEEN(TO_DATE('20150701','yyyymmdd'), TO_DATE('20150410','yyyymmdd')) FROM dual; -- 2.70967741
SELECT MONTHS_BETWEEN(TO_DATE('20150701','yyyymmdd'), TRUNC(TO_DATE('20150410 15:23:38','yyyymmdd HH24:mi:ss'))) FROM dual; -- 2.70967741
最佳實踐: 先用 TRUNC 將比對雙方的時間統一截至「日」精度,再套用 MONTHS_BETWEEN,即可精準界定「上季度前 10 天」的邊界,避免時分秒干擾。
TRUNC(date):最簡潔的「去時分秒」方法,保留 DATE 型別。MONTHS_BETWEEN:計算兩日期相差月數,極敏感於時分秒,配合 TRUNC 使用才能獲得確定性結果。