日期時間處理是商業應用最高頻需求:帳齡分析、SLA 計算、分區裁剪、資料歸檔、跨時區報表。DB2 提供豐富的標準 SQL 函數與擴展語法,**善用內建函數可避免應用層迴圈、減少網路傳輸、啟用分區裁剪**,是 SQL 效能優化關鍵。
| 函數 | 輸入類型 | 回傳類型 | 說明與範例 |
|---|---|---|---|
YEAR(dt) | DATE/TIMESTAMP | INTEGER | 年份。`YEAR(CURRENT DATE)` → 2024 |
MONTH(dt) | DATE/TIMESTAMP | INTEGER | 月份 1-12 |
DAY(dt) | DATE/TIMESTAMP | INTEGER | 日期 1-31 |
HOUR(ts) | TIME/TIMESTAMP | INTEGER | 小時 0-23 |
MINUTE(ts) | TIME/TIMESTAMP | INTEGER | 分鐘 0-59 |
SECOND(ts) | TIME/TIMESTAMP | INTEGER | 秒 0-59 |
MICROSECOND(ts) | TIMESTAMP | INTEGER | 微秒 0-999999 |
| 函數 | 用途 | 範例 |
|---|---|---|
VARCHAR_FORMAT(ts, 'format') / TO_CHAR(ts, 'format') | 時間戳 → 格式化字串 | `VARCHAR_FORMAT(CURRENT TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')` → '2024-01-15 14:30:45' |
TIMESTAMP_FORMAT(str, 'format') / TO_DATE(str, 'format') | 字串 → 時間戳 | `TIMESTAMP_FORMAT('20240115143045', 'YYYYMMDDHH24MISS')` |
DATE('2024-01-15') | 字串/數值 → DATE | 支援 'YYYY-MM-DD', 'MM/DD/YYYY', 'YYYYMMDD' 等多種格式 |
TIME('14:30:45') | 字串 → TIME | 支援 'HH:MI:SS', 'HH.MI.SS' |
TIMESTAMP('2024-01-15 14:30:45') | 字串 → TIMESTAMP | 支援 ISO、ODBC、自訂格式 |
常用格式元素: `YYYY` 四位年、`MM` 兩位月、`DD` 兩位日、`HH24` 24小時、`MI` 分鐘、`SS` 秒、`MS` 毫秒、`US` 微秒、`Q` 季、`WW` 週、`D` 週幾(1-7, 1=Sun)、`DY` 週幾縮寫。
DB2 支援直觀的 **持續時間運算**:
-- 日期運算
CURRENT DATE + 1 YEAR
CURRENT DATE + 3 YEARS + 2 MONTHS + 15 DAYS
CURRENT DATE - 30 DAYS
-- 時間運算
CURRENT TIME + 5 HOURS - 30 MINUTES + 45 SECONDS
-- 時間戳運算
CURRENT TIMESTAMP + 2 DAYS - 3 HOURS
-- 複合:下月同一天
CURRENT DATE + 1 MONTH
-- 複合:本月最後一天
(LAST_DAY(CURRENT DATE)) -- 需 DB2 11.1+ 或自寫 UDF
規則: 年/月運算會處理月末邊界(1/31 + 1 MONTH = 2/28 或 2/29),日/時/分/秒為精確加減。
| 函數 | 回傳值 | 典型應用 |
|---|---|---|
DAYNAME(dt) | VARCHAR(混合大小寫) | 星期名稱:'Friday'、'星期五' |
MONTHNAME(dt) | VARCHAR | 月份名稱:'January'、'一月' |
DAYOFWEEK(dt) | 1-7 (1=Sun) | 美式週起算 |
DAYOFWEEK_ISO(dt) | 1-7 (1=Mon) | ISO 8601 週起算,歐洲/亞洲常用 |
DAYOFYEAR(dt) | 1-366 | 年度第幾天 |
WEEK(dt) | 1-54 (Sun起) | 年度第幾週 |
WEEK_ISO(dt) | 1-53 (Mon起) | ISO 週號 |
JULIAN_DAY(dt) | INTEGER | 儒略日數(自公元前4712-01-01),天文/跨曆法計算 |
MIDNIGHT_SECONDS(ts) | 0-86399 | 當日經過秒數,便於時段分組 |
DAYS(dt) | INTEGER | 日期轉整數(自 0001-01-01),可直接相減得天數差 |
TIMESTAMP_ISO(dt/tm/ts) | TIMESTAMP | 統一轉換為 TIMESTAMP(6),含微秒 |
DB2 透過特殊暫存器 `CURRENT TIMEZONE`(`DECIMAL(6,0)` 格式 `HHMMSS`,東正西負)支援時區感知:
-- 當地時間 → UTC/GMT
CURRENT TIMESTAMP - CURRENT TIMEZONE
CURRENT TIME - CURRENT TIMEZONE
-- UTC → 當地時間
CURRENT TIMESTAMP + CURRENT TIMEZONE
-- 具體時間戳轉換(假設資料庫在 UTC+8)
SELECT TS_UTC, TS_UTC + CURRENT TIMEZONE AS TS_LOCAL FROM T_LOG;
最佳實務: 資料庫層**統一儲存 UTC**(`TIMESTAMP WITH TIME ZONE` 在 DB2 11.5+ 支援),應用層/報表層按使用者時區轉換顯示。
-- 語法:TIMESTAMPDIFF(間隔類型, 字串化的時間戳差)
-- 間隔類型:1=微秒分數 2=秒 4=分 8=時 16=天 32=週 64=月 128=季 256=年
TIMESTAMPDIFF(16, CHAR(TIMESTAMP('2024-11-30') - TIMESTAMP('2024-11-08'))) -- 回傳 22 天
缺陷: 假設每月 30 天、每年 360 天,**不考慮閏年、實際月長**,跨月/跨年誤差大。
-- 精確秒差
(DAYS(TS1) - DAYS(TS2)) * 86400 + (MIDNIGHT_SECONDS(TS1) - MIDNIGHT_SECONDS(TS2))
-- 封裝為 UDF(建議部署於 SYSTOOLS 或應用 Schema)
CREATE FUNCTION SECONDS_DIFF(TS1 TIMESTAMP, TS2 TIMESTAMP)
RETURNS BIGINT
LANGUAGE SQL
DETERMINISTIC NO EXTERNAL ACTION
RETURN (DAYS(TS1) - DAYS(TS2)) * 86400 + (MIDNIGHT_SECONDS(TS1) - MIDNIGHT_SECONDS(TS2));
CREATE FUNCTION DAYS_IN_YEAR(YR INT)
RETURNS INT
LANGUAGE SQL
DETERMINISTIC NO EXTERNAL ACTION
RETURN CASE
WHEN MOD(YR, 400) = 0 THEN 366
WHEN MOD(YR, 100) = 0 THEN 365
WHEN MOD(YR, 4) = 0 THEN 366
ELSE 365
END;
反模式(導致全表掃描):
-- 函數套用在欄位上,無法使用索引
WHERE YEAR(ORDER_DATE) = 2024
WHERE DATE(ORDER_TS) = CURRENT DATE
WHERE ORDER_DATE + 30 DAYS > CURRENT DATE
正模式(SARGable,可索引範圍掃描、分區裁剪):
-- 使用常數範圍
WHERE ORDER_DATE >= '2024-01-01' AND ORDER_DATE < '2025-01-01'
WHERE ORDER_TS >= TIMESTAMP('2024-01-15 00:00:00') AND ORDER_TS < TIMESTAMP('2024-01-16 00:00:00')
WHERE ORDER_DATE > CURRENT DATE - 30 DAYS
原則: **欄位保持「裸露」,運算移至常數側**。優化器才能利用索引、範圍分區、MDC 區塊索引。
-- 1. 本月第一天 00:00:00
TIMESTAMP(DATE(CURRENT TIMESTAMP - (DAY(CURRENT TIMESTAMP)-1) DAYS), '00:00:00')
-- 2. 本月最後一天 23:59:59.999999
TIMESTAMP(LAST_DAY(CURRENT DATE), '23:59:59.999999')
-- 3. 上月同期範圍
WHERE ORDER_DATE BETWEEN
DATE(CURRENT DATE - 1 MONTH - (DAY(CURRENT DATE)-1) DAYS)
AND LAST_DAY(CURRENT DATE - 1 MONTH)
-- 4. 將字串 '20240115' 安全轉 DATE(含錯誤處理)
CASE
WHEN TRANSLATE('20240115', '', '0123456789') = '' AND LENGTH('20240115') = 8
THEN DATE(SUBSTR('20240115',1,4)||'-'||SUBSTR('20240115',5,2)||'-'||SUBSTR('20240115',7,2))
ELSE NULL
END
-- 5. 移除 TIMESTAMP 微秒部分
CURRENT TIMESTAMP - MICROSECOND(CURRENT TIMESTAMP) MICROSECONDS
DB2 日期時間函數族群完整支援 ANSI SQL:2011 標準並提供強大擴展。核心掌握三點:
將常用邏輯封裝為 UDF(`SECONDS_DIFF`、`DAYS_IN_YEAR`、`FIRST_DAY_OF_MONTH`),建立團隊函數庫,標準化開發、消除重複造輪子、避免時區 Bug。