現象:在多語言環境下使用過Oracle的同學想必都遇到過這樣一個問題,
- date_v date;
- date_v := to_date('2010/11/16');--或'2010/11/16'
同一個服務器,不同Oracle clinet 不一定都行得通。
原因: 不指定轉換字符串的情況下,Oracle使用既定的格式串進行日期轉換操作,執行SELECT * FROM NLS_SESSION_PARAMETERS;察看NLS信息,其中NLS_DATE_FORMAT是指當前使用的格式化字符串。我這裡是"DD-MON-RR",dd代表日rr代表年mon代表月,所以to_date('2010/11/16')==to_date('2010/11/16','DD-MON-RR'),
MON在NLS_LANGUAGE為AMERICAN的情況下只識別Nov而不是別11,所以轉換失敗。
解決: 既然知道問題原因了,解決這個問題有三個方法。
1,更改NLS_LANGUAGE會使NLS_DATE_FORMAT同步為相應的格式。我們這裡需要japanese格式。我通過增加環境變量(NLS_LANG=JAPANESE_JAPAN.JA16SJIS)實現。
NLS_DATE_FORMAT變成RR-MM-DD了,測試,select to_date('2010/11/15') from dual--ok。
2,增加環境變量NLS_DATE_FORMAT(RR-MM-DD或yy-mm-dd或yy/mm/dd任何你需要的格式)。
測試,select to_date('2010/11/15') from dual--ok。
這裡要注意經過我測試,rr和yy是一個意思 ,連接符號-和/都可以使轉換成功。
更改NLS_LANGUAGE或NLS_DATE_FORMAT的方法可以google以下.
到這裡基本大功告成了,但是你也許會說我們不能隨便修改Oracle clIEnt的這些配置,因為其他實例也許需要它!下面是第三個方法
3,to_date有個重載方法,提供轉換需要的format。
無論NLS_DATE_FORMAT是什麼,select to_date('2010/11/15','yy/mm/dd') from dual--ok。
但是還是不要高興過早了,select to_date('2010/nov/15','yy/mon/dd') from dual--不一定ok。
當NLS_LANGUAGE環境為AMERICAN時是OK的,為East Asia language時mon格式符真正識別的是11月(這裡japan和china還是不一樣的,看起來是一個字,但是從我機器輸入select to_date('2010/11月/15','yy/mon/dd') from dual卻出錯。
具體細節可以參考文章http://www.eygle.com/special/NLS_CHARACTER_SET_05.htm
現在問題明白了,但解決方法這麼多,難道轉換一個字符串到日期真的這麼周折?
我選擇第三個方法,因為它是我能控制的。select to_date('2010/11/15','yy/mm/dd') from dual--ok,因為mm的轉譯永遠是阿拉伯數字啊。
案例:這樣一個情景,現在有個存儲要部署在未知地理未知服務器上,它需要一個date參數,plsql程序員想當然的吧它定義成了date,不巧,調用存儲的另一個程序員是個想通過腳本來調用存儲,並且通過Windows計劃任務觸發腳本執行,這個腳本給了一個2010/11/15,看起來沒任何問題,但是不巧這台應用服務器的Oracle客戶端的NLS_LANGUAGE=AMERICAN,真是無巧不成書啊,當Oracle做這樣一個調用前的參數轉換工作的時候 date_v date; date_v := '2010/11/16'(這個轉換是默認的不可控的);災難來臨了。
怎麼避免這樣的災難呢?參數改成nvarchar吧,自己做參數轉換,使用上面我們的第三個解決方法data_v:= to_date(data_vchar,'yy/mm/dd'),其實災難可以避免。
有人會說,調用存儲的人傳了個2010¥/11……/16!怎麼辦,你的yyyy/mm/dd如何處理?當然沒法處理,我的方法是給出一個准確的文檔告訴對方參數應該是什麼格式的,出錯了也可以排除責任了。 另外我自己轉換可以添加一些異常機制來提示問題,Oracle自動轉換出錯了可是很霸道的。