sql >> データベース >  >> RDS >> Oracle

DSTのためにOracleの日付比較が壊れています

    このエラーを回避するには、次のように、where句の式をタイムスタンプタイプ(タイムゾーンなしのタイムスタンプ)に明示的にキャストすることを検討してください。

    select * 
    from MY_TABLE T
    where T.MY_TIMESTAMP >= cast(CURRENT_TIMESTAMP - interval '1' hour As timestamp );
    

    または、セッションのタイムゾーンを明示的に設定できます。たとえば、ニューヨークの標準(冬)時間の場合は、
    ALTER SESSION time_zone ='-05:00' > 、またはすべてのクライアントの環境でORA_SDTZ環境変数を設定することにより、
    詳細については、次のリンクを参照してください:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

    しかし、それは本当に何に依存するかもしれません テーブルのタイムスタンプ列に保存されます。たとえば、タイムスタンプ 2014-07-01 15:00:00 実際、それは「冬時間」なのか「夏時間」なのか?

    CURRENT_TIMESTAMP functionは、データ型TIMESTAMP WITH TIME ZONEの値を返します
    次のリンクを参照してください:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

    タイムスタンプと日付を比較して、Oracleは暗黙的にデータをより正確なデータ型に変換しますセッションタイムゾーンを使用します!
    このリンクを参照してください->http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

    この特定のケースでは、Oracleはキャストしますタイムスタンプ タイムスタンプ付きタイムスタンプへの列 タイプ。

    Oracleは、クライアント環境からセッションのタイムゾーンを決定します。
    次のクエリを使用して、現在のセッションのタイムゾーンを決定できます。

    select sessiontimezone from dual;
    

    たとえば、私のPC(Win 7)では、[夏時間の時計を自動的に調整する]オプションをオンにすると、このクエリは(SQLDeveloperの下で)次のように返されます。

    SESSIONTIMEZONE                                                           
    ---------------
    Europe/Belgrade 
    


    Windowsでこのオプションのチェックを外してからSQLDeveloperを再起動すると、次のようになります。

    SESSIONTIMEZONE                                                           
    ---------------
    +01:00     
    

    以前のセッションのタイムゾーンはリージョン名のタイムゾーンであり、Oracleは日付の計算でこのリージョンの夏時間ルールを使用します。

    alter session set time_zone = 'Europe/Belgrade';
    select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
           cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
    from dual;
    
    session SET altered.
    X                            Y                          
    ---------------------------- ----------------------------
    2014-01-29 01:30:00 EUROPE/B 2014-05-29 01:30:00 EUROPE/B 
    ELGRADE                      ELGRADE       
    


    後者のタイムゾーンは、固定オフセット「+01:00」(常に「冬時間」)を使用し、OracleはそれにDSTルールを適用せず、固定オフセットを追加するだけです。

    alter session set time_zone = '+01:00';
    select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
           cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
    from dual;
    
    session SET altered.
    X                            Y                          
    ---------------------------- ----------------------------
    2014-01-29 01:30:00 +01:00   2014-05-29 01:30:00 +01:00  
    

    好奇心のために、 Y 上記の結果は、2つの異なる時間を表しています!!!
    014-05-29 01:30:00 EUROPE / BELGRADE と同じではありません: 2014-05-29 01:30:00 +01:00

    しかし実際にはこれ:
    014-05-29 01:30:00 EUROPE / BELGRADE 等しい: 2014-05-29 01:30:00 +02:00

    上記は、単純な「ボックスのチェック解除」がクエリにどのように影響するか、およびユーザーが「このクエリは1月に正常に機能しましたが、 7月に間違った結果」。

    そしてまだORA-01878のトピックについて-私のセッションがEUROPE/ Warsawだとしましょう 私のテーブルにはこのタイムスタンプが含まれています(タイムゾーンなし)

    'TIMESTAMP'2014-03-30 2:30:00'
    

    私の地域では、2014年のDSTの変更は、3月30日の午前2時に発生します。
    これは、3月30日の夜の2:00に起きて、時計をシフトする必要があることを意味します。 2:00から3:00に転送;)

    alter session set time_zone = 'Europe/Warsaw';
    select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
    from dual;
    
    SQL Error: ORA-01878: podane pole nie zostało znalezione w dacie-godzinie ani w interwale
    01878. 00000 -  "specified field not found in datetime or interval"
    *Cause:    The specified field was not found in the datetime or interval.
    *Action:   Make sure that the specified field is in the datetime or interval.
    

    Oracleは、このタイムスタンプが私の地域では無効であることを知っています DSTの規則によると、3月30日の2:30には時間がないため、2:00に時計は3:00に移動し、2:30には時間がありません。したがって、OracleはエラーORA-01878をスローします。

    ただし、このクエリは完全に正常に機能します:

    alter session set time_zone = '+01:00';
    select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
    from dual;
    
    session SET altered.
    X                          
    ----------------------------
    2014-03-30 02:30:00 +01:00 
    

    これがこのエラーの理由です。テーブルに2014-03-092:30のようなタイムスタンプが含まれています かそこら(ニューヨークの場合、DSTシフトは3月9日と11月2日に発生します)、Oracleはそれらをタイムスタンプ(TZなし)からTZ付きタイムスタンプに変換する方法を知りません。

    最後の質問-なぜ>=のクエリ は機能しませんが、 <=を使用したクエリ 正常に動作しますか?

    SQLDeveloperは最初の50行(おそらく100行?設定によって異なります)のみを返すため、動作します/動作しません。クエリはテーブル全体を読み取るのではなく、最初の50(100)行がフェッチされると停止します。
    「動作中」のクエリを次のように変更します。

    select sum( EXTRACT(HOUR from MY_TIMESTAMP) ) from MY_TABLE 
    where MY_TIMESTAMP <= (CURRENT_TIMESTAMP - interval '1' hour );
    

    これにより、クエリはテーブル内のすべての行を読み取るようになり、エラーが表示されます。100%確信しています。



    1. PHPとmysqlを使用して無制限のレベルのメニューを作成する方法

    2. SQL Serverデータベース(T-SQL)にデータファイルを追加する方法

    3. 管理者でデータベースを管理する方法

    4. SQL Server(T-SQL)でのTRANSLATE()関数のしくみ