ORA-01843: 無效的月份

Rick Carter發表於2024-11-27

上個文章介紹了動態LINQ庫
然後動態造了一個查詢,示例如下:

//ctx是EF的DbContext,欄位Value是字串型別
await ctx.Tables.Where("As(Value,\"DateTime?\")>@0",datetime).ToListAsync();

上面的查詢條件在Oracle下大概是這樣:CAST("Value" AS TIMESTAMP(7))>:datetime
然後報錯ORA-01843: 無效的月份,原因是Value欄位的值存的是這樣的格式:"yyyy-MM-dd hh:mm:ss",oracle無法識別這樣格式的時間值,那麼它能識別什麼樣的時間值呢,查下。

SELECT * FROM v$nls_parameters;

可以看到裡面有很多環境引數,其中有NLS_TIMESTAMP_FORMAT和NLS_TIMESTAMP_TZ_FORMAT是我們需要注意的,它預設安裝下是個很奇怪的時間格式,我們需要修改他們。

alter session set NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS.FF";
alter session set NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS.FF";

這樣就能識別Value中的時間值了,不再報錯。
不過需要注意的是這個僅僅是當前會話內有效,如果是EF中需要像下面的這樣做。

await ctx.OpenConnectionAsync();
ctx.Database.ExecuteSqlRawAsync("alter session set NLS_TIMESTAMP_FORMAT=\"YYYY-MM-DD HH24:MI:SS.FF\";");
ctx.Database.ExecuteSqlRawAsync("alter session set NLS_TIMESTAMP_TZ_FORMAT=\"YYYY-MM-DD HH24:MI:SS.FF\";");
await ctx.Tables.Where("As(Value,\"DateTime?\")>@0",datetime).ToListAsync();
await ctx.CloseConnectionAsync();//注意這句需要放到finally中

你也可以修改oralce全域性NLS_TIMESTAMP_FORMAT環境引數,那麼程式碼就不需要這麼改了。
如果是直接sql查詢,可以修改sql語句的情況下,還可以用TO_TIMESTAMP方法使用指定的格式化字串將Value欄位轉成TIMESTAMP,也能解決問題。

相關文章