18

Oracle 10g には、特定の操作にかかった時間を示すタイムスタンプを保持するテーブルがあります。starttime と endtime の 2 つのタイムスタンプ フィールドがあります。これらのタイムスタンプによって与えられる期間の平均を見つけたいです。私は試します:

select avg(endtime-starttime) from timings;

しかし、得る:

SQL エラー: ORA-00932: 一貫性のないデータ型: 予想される NUMBER は INTERVAL DAY TO SECOND を取得しました

これは機能します:

select
     avg(extract( second from  endtime - starttime) +
        extract ( minute from  endtime - starttime) * 60 +
        extract ( hour   from  endtime - starttime) * 3600) from timings;

しかし、本当に遅いです。

間隔を秒数に変換するより良い方法、またはこれを行う他の方法はありますか?

編集:これを本当に遅らせていたのは、開始時間の前にいくつかの終了時間があったという事実でした。何らかの理由で、この計算は非常に遅くなりました。私の根本的な問題は、クエリ セットからそれらを削除することで解決されました。この変換を簡単にする関数も定義しました。

FUNCTION fn_interval_to_sec ( i IN INTERVAL DAY TO SECOND )
RETURN NUMBER
IS
  numSecs NUMBER;
BEGIN
  numSecs := ((extract(day from i) * 24
         + extract(hour from i) )*60
         + extract(minute from i) )*60
         + extract(second from i);
  RETURN numSecs;
END;
4

6 に答える 6

25

複数の抽出を使用するヘアリーな式よりも、OracleでDATETIMEの差を秒単位で取得するための、より短く、より速く、より優れた方法があります。

これを試して、応答時間を秒単位で取得してください。

(sysdate + (endtime - starttime)*24*60*60 - sysdate)

また、タイムスタンプを減算するときに秒の小数部分を保持します。

詳細については、 http://kennethxu.blogspot.com/2009/04/converting-oracle-interval-data-type-to.htmlを参照してください。


カスタムのpl/sql関数にはパフォーマンスのオーバーヘッドが大きく、重いクエリには適さない場合があることに注意してください。

于 2011-12-06T22:22:35.590 に答える
9

終了時間と開始時間が互いに 1 秒以内でない場合は、タイムスタンプを日付としてキャストし、日付演算を行うことができます。

select avg(cast(endtime as date)-cast(starttime as date))*24*60*60 
  from timings;
于 2009-01-16T15:10:58.367 に答える
2

OracleでINTERVAL DAY TO SECONDを明示的に変換する関数はないようです。このドキュメントNUMBERの最後にある表を参照してください。これは、そのような変換がないことを示しています。

INTERVAL DAY TO SECOND他の情報源は、使用している方法がデータ型 から数値を取得する唯一の方法であることを示しているようです。

この特定のケースで試すことができる他の唯一のことは、それらを減算する前に数値に変換することですが、それは2倍のextractイオンを行うため、さらに遅くなる可能性があります:

select
     avg(
       (extract( second from endtime)  +
        extract ( minute from endtime) * 60 +
        extract ( hour   from  endtime ) * 3600) - 
       (extract( second from starttime)  +
        extract ( minute from starttime) * 60 +
        extract ( hour   from  starttime ) * 3600)
      ) from timings;
于 2009-01-16T15:01:47.777 に答える
2

SQL フィドル

Oracle 11g R2 スキーマのセットアップ:

カスタム集計を実行するときに使用するタイプを作成します。

CREATE TYPE IntervalAverageType AS OBJECT(
  total INTERVAL DAY(9) TO SECOND(9),
  ct    INTEGER,

  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT IntervalAverageType
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT IntervalAverageType,
    value       IN     INTERVAL DAY TO SECOND
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT IntervalAverageType,
    returnValue    OUT INTERVAL DAY TO SECOND,
    flags       IN     NUMBER
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT IntervalAverageType,
    ctx         IN OUT IntervalAverageType
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY IntervalAverageType
IS
  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT IntervalAverageType
  ) RETURN NUMBER
  IS
  BEGIN
    ctx := IntervalAverageType( INTERVAL '0' DAY, 0 );
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT IntervalAverageType,
    value       IN     INTERVAL DAY TO SECOND
  ) RETURN NUMBER
  IS
  BEGIN
    IF value IS NOT NULL THEN
      self.total := self.total + value;
      self.ct    := self.ct + 1;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT IntervalAverageType,
    returnValue    OUT INTERVAL DAY TO SECOND,
    flags       IN     NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    IF self.ct = 0 THEN
      returnValue := NULL;
    ELSE
      returnValue := self.total / self.ct;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT IntervalAverageType,
    ctx         IN OUT IntervalAverageType
  ) RETURN NUMBER
  IS
  BEGIN
    self.total := self.total + ctx.total;
    self.ct    := self.ct + ctx.ct;
    RETURN ODCIConst.SUCCESS;
  END;
END;
/

次に、カスタム集計関数を作成できます。

CREATE FUNCTION AVERAGE( difference INTERVAL DAY TO SECOND )
RETURN INTERVAL DAY TO SECOND
PARALLEL_ENABLE AGGREGATE USING IntervalAverageType;
/

クエリ 1 :

WITH INTERVALS( diff ) AS (
  SELECT INTERVAL '0' DAY FROM DUAL UNION ALL
  SELECT INTERVAL '1' DAY FROM DUAL UNION ALL
  SELECT INTERVAL '-1' DAY FROM DUAL UNION ALL
  SELECT INTERVAL '8' HOUR FROM DUAL UNION ALL
  SELECT NULL FROM DUAL
)
SELECT AVERAGE( diff ) FROM intervals

結果

| AVERAGE(DIFF) |
|---------------|
|     0 2:0:0.0 |
于 2017-12-07T10:08:43.127 に答える
1

さて、これは非常に手っ取り早い方法ですが、秒の差を別の列に保存し (レコードが変更された場合は、トリガーを使用するか、これを手動で更新する必要があります)、その列を平均化するのはどうでしょうか?

于 2009-01-16T15:05:07.770 に答える