web-dev-qa-db-ja.com

時間間隔を平均化する方法は?

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

select avg(endtime-starttime) from timings;

しかし、取得:

SQLエラー:ORA-00932:一貫性のないデータ型:予期されたNUMBERがINTERVAL DAY TOSECONDを取得しました

これは機能します:

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;
15
Justsalt

最もクリーンな方法は、これを行うための独自の集計関数を作成することです。これは、これを最もクリーンに処理するためです(1秒未満の解像度などを処理します)。

実際、この質問は asktom.Oracle.com しばらく前に質問(および回答)されました(記事にはソースコードが含まれています)。

7

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

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

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

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

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


カスタムpl/sql関数にはかなりのパフォーマンスオーバーヘッドがあります これは重いクエリには適さない可能性があることに注意してください。

23
Vadzim

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

select avg(cast(endtime as date)-cast(starttime as date))*24*60*60 
  from timings;
9
jimmyorr

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

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

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

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;
2
Adam Bellaire

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 |
2
MT0

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

1
Bork Blatt