Q&A

回答の並べ替え:
投稿新規に質問を投稿する

Oracle:Date型への時刻保存ができない

fujiima fujiima

2020-09-01 15:46

Oracleの Date型カラムに対し、
アクション「テーブル XX のデータを更新する」で「=NOW()」などを保存すると
【ORA-01861: リテラルが書式文字列と一致しません】エラーが発生します。

年月日+時分秒の数字6つの組み合わせが駄目みたいで、数字3つだけなら
下記のパターンのように保存されます。

「="2020/09/01"」→ '2020/09/01'
「="01:02:03"」 → '2001/02/03'
「=DATE(1,2,3)」→ '2001/02/03'
「=TIME(4,5,6)」→ '2004/05/06'


一方でアクション「テーブル XX から1件取得する」で、Date型カラムから
時刻付きの値を拾ってくると、時刻付きのまま正常に表示されます。

また Oracleの Timestamp型が相手だと、日付&時刻の組み合わせでも正常に保存されます。
だからと言って、テーブル構造をCELFの都合に合わせて変更などできませんが。


Oracleの Date型は日付&時刻ですが、MySQLの Date型は時刻を含みませんので、
CELFによる保存時、相手がOracleでもMySQL向けの加工処理をしている?と予想します。


CELFからの書き込み用テーブルを作って、トリガー経由で値をコピーしてやるぐらいしか
対処法が見つからなくて困っております・・・どうかお力添えください。

River River
fujiimaさん

ご返信ありがとうございます。

>クライアント接続時(ODBC)は、環境変数 NLS_DATE_FORMAT で対応可能。
>サーバー接続時(JDBC)は NLS_DATE_FORMAT での対応はできない。
オンプレミス版でしたか、、、直接の解決には至らず残念です。
今後、弊社でもオンプレミス版を扱う可能性はある為、
情報の共有ありがとうございました。
確かに他の手段がなさそうなので、仕方ないけれど、
「書き込み用テーブルを作って、トリガー経由で値をコピー」ってなんか悲しいですね。

>CELFのSQL実行アクションが、SELECT文以外も実行できるようになれば
>次のコマンドで解決できるかもしれないのですけど!
>「ALTER SESSION SET NLS_DATE_FORMAT='YYYY/MM/DD HH24:MI:SS';」
同じことを思って実際に試したりしたんですが、やっぱりSELECT限定なんですよね。
SELECT文以外も実行出来ればよいですよね。
fujiima fujiima
Riverさん

わざわざ検証をしていただきありがとうございます。
非常に参考になります。


【クライアント接続時】
こちらではまず V$NLS_PARAMETERS の値を CELF で表示させてみました。
何もしないと NLS_DATE_FORMAT の値は "RR-MM-DD" でした。

次に、私の端末の環境変数に NLS_DATE_FORMAT を追加し、curlも再起動しました。
コマンド:「SET NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS」

結果、ODBC接続している場合には、設定した値 "YYYY-MM-DD HH24:MI:SS" が
V$NLS_PARAMETERS にて無事に取得できました。ありがとうございます。


【サーバー接続時】
ところで、弊社ではオンプレミスで導入しているため、ユーザー向けには
DB接続は「サーバー接続」を使用しています。クライアント接続ではなく。

そこで期待を込めてCELFサーバにも、同環境変数を入れてみましたが、
残念ながら効果はありませんでした。

クライアント接続はODBCドライバ経由ですが、
サーバー接続はCELFサーバ側での、JDBCドライバ経由です。

調べてみるとどうも、JDBCはNLS系環境変数は無視するようです。代わりに
ロケールに合わせて自動設定した結果、日本なら "RR-MM-DD" になるようです。

https://docs.oracle.com/cd/E11882_01/java.112/e16548/global.htm#JJDBC28644
"The JDBC driver does not check NLS environment."

https://docs.oracle.com/cd/E96517_01/xeinl/setting-client-connection-language-preferences.html
"Oracle Databaseへの接続にOracle JDBCを使用するJavaアプリケーションでは、NLS_LANGを使用しません。"
"Java VMのデフォルトのロケールをOracle Databaseのlanguageとterritoryの設定にマップします。"

https://qiita.com/neko_the_shadow/items/baca14a933ddc9d3d562
https://docs.oracle.com/cd/E16338_01/server.112/b56307/ch3globenv.htm


【結論】
クライアント接続時(ODBC)は、環境変数 NLS_DATE_FORMAT で対応可能。
サーバー接続時(JDBC)は NLS_DATE_FORMAT での対応はできない。
CELFサーバはJDBCの仕様に従っているだけなのでイノセント。

というわけで、サーバー接続時は今のところ最初に検討した
「書き込み用テーブルを作って、トリガー経由で値をコピー」
で対応するしかなさそうだなという結論です。


CELFのSQL実行アクションが、SELECT文以外も実行できるようになれば
次のコマンドで解決できるかもしれないのですけど!
「ALTER SESSION SET NLS_DATE_FORMAT='YYYY/MM/DD HH24:MI:SS';」


ありがとうございました。
River River
メダルベストアンサー
fujiimaさん

ローカルPC(Windows10)にOracle18cExpressを
インストールした環境で試したことを共有します。

以下の環境変数を登録する前は、
同じく【ORA-01861: リテラルが書式文字列と一致しません】が発生しましたが、
PCの環境変数に以下の変数を追加した場合、「=NOW()」のセルでも更新できました。

変数名:NLS_DATE_FORMAT
変数値:YYYY-MM-DD HH24:MI:SS

<注意>
ただし、ご利用の端末から他のシステムのOracleへ接続する場合も
上記内容で接続されますので、影響がでるかもしれません。
変更される際はご確認のうえ、ご自身の責任範囲でご変更頂ければと思います。

ご参考になれば幸いです。