日期字串與 Oracle 格式模型比對失敗後重新對齊並轉成資料庫日期型別的示意

ORA-01861 日期格式錯誤:Oracle 原因與完整解決方法

ORA-01861 表示輸入的文字值與日期或時間戳記的格式模型不相符;修正方式是讓輸入內容與格式模型完全一致,或改用正確型別的綁定值。先確認實際資料型別,再檢查字串的年、月、日、時間、時區與格式模型,通常就能快速定位問題。

ORA-01861 是什麼?

Oracle 對 ORA-01861 的官方說明是:文字值必須符合格式字串;若使用 FX 精確比對修飾詞,除了前導空白外,文字值必須逐字元符合格式。官方建議的處理方式是更正格式字串,使其符合輸入的文字值。可參考 Oracle ORA-01861 官方錯誤說明

這個錯誤多半發生在字串被轉成 DATETIMESTAMP 或帶時區的時間戳記時,也可能源自 Oracle 自動進行的隱式轉換。重點不是只看畫面上顯示的日期,而是確認 SQL 執行當下的值、型別與格式模型。

先用 30 秒快速排查

  1. 印出或記錄實際輸入字串,不要只看程式碼中的預期值。確認分隔符號、年份位數、月份順序、時間、毫秒與時區是否存在。
  2. 把輸入逐段對照格式模型。例如 2026-07-10 對應 YYYY-MM-DD,而不是 DD-MM-YYYY
  3. 確認資料庫欄位與綁定參數的型別是 DATETIMESTAMP 還是 VARCHAR2。日期欄位不應先被當成文字再轉回日期。
  4. 找出是否有隱式轉換,例如日期欄位與字串常值直接比較。這類 SQL 會受到工作階段的 NLS 設定影響。

常見錯誤與正確寫法

輸入是年、月、日順序時,格式模型也必須採相同順序。以下第一行會失敗,第二行才是正確寫法:

SELECT TO_DATE('2026-07-10', 'DD-MM-YYYY') FROM dual;
SELECT TO_DATE('2026-07-10', 'YYYY-MM-DD') FROM dual;

不要用字串與日期欄位比較,因為 Oracle 可能依 NLS_DATE_FORMAT 將字串隱式轉換。查詢某一個日曆日的資料時,也不要只用等號,否則帶有時間部分的資料可能被漏掉。ANSI 日期常值配合左閉右開區間更穩定:

SELECT *
FROM orders
WHERE created_at >= DATE '2026-07-10'
  AND created_at <  DATE '2026-07-11';

如果 created_at 本身是 DATE,絕對不要寫 TO_DATE(created_at, ...)TO_DATE 的用途是把字串轉成日期;套在日期欄位上可能先把日期隱式轉成字串,再轉回日期,結果依賴 NLS 設定。只有在顯示輸出時才使用 TO_CHAR

SELECT TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI:SS')
FROM orders;

包含時分秒的字串應使用對應的時間戳記格式:

SELECT TO_TIMESTAMP(
  '2026-07-10 14:30:15',
  'YYYY-MM-DD HH24:MI:SS'
) FROM dual;

ISO 8601 字串若帶有數字時區偏移,可使用 TO_TIMESTAMP_TZ。格式模型中的字面 T 必須加上雙引號,時區則由 TZH:TZM 接收:

SELECT TO_TIMESTAMP_TZ(
  '2026-07-10T14:30:15+08:00',
  'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'
) FROM dual;

FX 會啟用精確格式比對,適合輸入規格固定、需要拒絕寬鬆解析的情境。啟用後,標點符號與數字寬度必須符合格式;若還要允許省略前導零,可配合相關格式修飾詞設計,但不要把不固定的輸入誤當固定格式。

SELECT TO_DATE('2026-07-10', 'FXYYYY-MM-DD') FROM dual;

日期與時間元素、修飾詞及限制可查閱 Oracle Database 19c SQL 格式模型文件

檢查 NLS_DATE_FORMAT

下列查詢可查看目前工作階段的日期相關設定:

SELECT parameter, value
FROM nls_session_parameters
WHERE parameter IN (
  'NLS_DATE_FORMAT',
  'NLS_TIMESTAMP_FORMAT',
  'NLS_TIMESTAMP_TZ_FORMAT',
  'NLS_DATE_LANGUAGE'
)
ORDER BY parameter;

NLS_DATE_FORMAT 是工作階段設定,不同用戶端、連線池或登入觸發器可能建立不同值。因此,依賴預設格式的 SQL 很脆弱:同一段文字在某個工作階段成功,在另一個工作階段可能得到 ORA-01861。轉換文字時應明確指定格式模型;應用程式傳參數時則優先傳送具型別的日期或時間戳記值。

JDBC/Spring 應用程式的正確處理方式

不要把日期字串串接進 SQL。JDBC 應使用預備敘述與型別綁定,例如只有日期時使用 PreparedStatement.setDate,包含時間時使用 setTimestamp;若採用現代 JDBC,也可依驅動程式支援情況以 setObject 綁定適合的 java.time 型別。Spring 的參數化查詢也應傳入明確型別,而非拼接文字。

SELECT *
FROM orders
WHERE created_at >= ?
  AND created_at < ?

SQL Developer 與應用程式看似執行相同 SQL,結果卻不同,常見原因是兩者建立了不同工作階段,具有不同 NLS 設定;另一個原因是 SQL Developer 使用文字常值,而程式實際送入的是不同 JDBC 綁定型別。比較問題時,要同時記錄 SQL、綁定值、綁定型別及工作階段 NLS 參數。

排查清單

  1. 確認錯誤發生的 SQL 與實際綁定值。
  2. 確認欄位、運算式和參數的資料型別。
  3. 逐段比對輸入與格式模型,包括標點、寬度、時間和時區。
  4. 移除字串與日期之間的隱式轉換。
  5. 日期篩選改用具型別的常值、綁定值或半開區間。
  6. 檢查目前工作階段的 NLS 設定。
  7. 分別在 SQL 工具與應用程式記錄綁定型別後重測。

常見問題

TO_DATE 與 TO_CHAR 有什麼不同?

TO_DATE 把字串依指定格式解析成 DATETO_CHAR 把日期或時間戳記格式化為顯示文字。不要用 TO_DATE 格式化日期,也不要把 TO_CHAR 的結果當成日期繼續計算。

為什麼相同 SQL 在一個環境成功,另一個環境失敗?

最常見原因是工作階段 NLS 設定不同,或用戶端送入的綁定型別不同。若 SQL 依賴隱式轉換,環境差異就會改變解析結果。

建議修改 NLS_DATE_FORMAT 嗎?

不建議把它當成主要修正。修改工作階段設定只能暫時配合某種輸入,還可能掩蓋其他程式的型別錯誤。較可靠的做法是明確指定格式模型,或直接使用具型別的綁定值。

結論

處理 ORA-01861 時,依序核對資料型別、實際輸入與格式模型,並移除隱式轉換。字串解析要明確指定格式,日期條件要使用日期常值或型別綁定,輸出顯示才使用 TO_CHAR。當三者一致,錯誤通常能被穩定排除,也不會因工具、連線池或 NLS 環境不同而再次出現。

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *