2

次のロジックを持つ巨大なレガシー PL/SQL プロシージャに問題があります。

l_elapsed := dbms_utility.get_time - l_timestamp;

wherel_elapsedおよびl_timestampはタイプであり、前の呼び出しの結果を保持しPLS_INTEGERますl_timestampget_time

この行は、バッチ実行中に突然失敗し始めましたORA-01426: numeric overflow

のドキュメントget_timeは少しあいまいですが、おそらく意図的にそうしていますが、戻り値に絶対的な意味はなく、ほとんどすべての数値である可能性があることを強く示唆しています。PLS_INTEGERそのため、32ビット整数のみをサポートできるに割り当てられているのを見るのは疑わしいです。しかし、インターウェブには、まさにこの種のことをしている人々の例がたくさんあります.

get_time手動で呼び出したときに喫煙銃が見つかりました。値-214512572を返しています。これは、32 ビット符号付き整数の最小値に疑わしいほど近い値です。最初の呼び出しと次の呼び出しの間に経過した時間の間にget_time、Oracle の内部カウンターが最大値と最小値からロールオーバーし、一方を他方から減算しようとするとオーバーフローが発生したのではないかと思っています。

これはありそうな説明ですか?get_timeもしそうなら、これは関数に固有の欠陥ですか? 今夜バッチが再び失敗するかどうかを待つこともできますが、それまでにこの動作の説明を知りたいと思っています.

4

3 に答える 3

3

遅いかもしれませんが、これは同じ質問を検索する人に役立つかもしれません.

基礎となる実装は単純な 32 ビット バイナリ カウンターで、データベースが最後に起動された時点から 100 秒ごとにインクリメントされます。

このバイナリ カウンタは、符号付き 32 ビット整数である PL/SQL BINARY_INTEGER 型にマップされています (64 ビット マシンで 64 ビットに変更された兆候はありません)。

したがって、クロックがゼロから始まると仮定すると、約 248 日後に +ve 整数制限に達し、反転して -ve 値がゼロに戻ります。

幸いなことに、両方の数値が同じ符号であれば、単純な減算を行って期間を求めることができます。それ以外の場合は、32 ビットの剰余を使用できます。

    IF SIGN(:now) = SIGN(:then) THEN
        RETURN :now - :then;
    ELSE
        RETURN MOD(:now - :then + POWER(2,32),POWER(2,32));
    END IF;

編集:このコードは、時間のギャップが大きすぎる場合(248日)、int制限を超えて失敗しますが、GET_TIMEを使用して期間を日単位で比較するべきではありません(以下を参照)。

最後に、なぜ GET_TIME を使用するのかという問題があります。

歴史的には、1 秒未満の時間を取得する唯一の方法でしたが、SYSTIMESTAMP の導入以来、GET_TIME を使用する唯一の理由は、GET_TIME が高速であるためです。これは、32 ビット カウンターの単純なマッピングであり、実際の値はありません。型変換であり、基礎となる OS クロック関数に影響を与えません (SYSTIMESTAMP のようです)。

相対時間のみを測定するため、2 点間の時間を測定するためだけに使用されます。かなりの時間がかかるタスク (1/1000 秒程度) では、代わりにタイムスタンプを使用するコストはわずかです。

実際に役立つ場面はごくわずかです (私が見つけた唯一の例は、アクセスごとにクロック ヒットを行うことが重要になるキャッシュ内のデータの経過時間をチェックすることです)。

于 2010-06-24T13:51:01.473 に答える
2

10gのドキュメントから:

数値は、プラットフォームとマシンに応じて -2147483648 から 2147483647 の範囲で返されます。アプリケーションは、間隔を決定する際に数値の符号を考慮する必要があります。たとえば、2 つの負の数値の場合、アプリケーション ロジックでは、最初の (前の) 数値が 0 に近い 2 番目の (後の) 数値よりも大きくなるようにする必要があります。同様に、アプリケーションでは、最初の (前の) 数値が負で、2 番目の (後の) 数値が正であることも許可する必要があります。

dbms_utility.get_timeそのため、 の結果を aに代入することは安全ですPLS_INTEGERが、バッチ実行の実行中にオーバーフローが発生する可能性は理論的にはあります (ただし、可能性は低いです)。2 つの値の差は 2^31 よりも大きくなります。

ジョブに多くの時間がかかる場合 (そのため、オーバーフローが発生する可能性が高くなります)、TIMESTAMP データ型に切り替えることができます。

于 2009-07-06T10:51:17.493 に答える
0

PLS_INTEGER 変数に負の値を割り当てると、ORA-01426 が発生します。

SQL> l
  1  declare
  2    a pls_integer;
  3  begin
  4    a := -power(2,33);
  5* end;
SQL> /
declare
*
FOUT in regel 1:
.ORA-01426: numeric overflow
ORA-06512: at line 4

ただし、-214512572 は -2^31 に近いと示唆しているようですが、数字を入力するのを忘れない限り、そうではありません。私たちは喫煙銃を見ていますか?

よろしく、ロブ。

于 2009-07-06T11:03:04.177 に答える