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;
最もクリーンな方法は、これを行うための独自の集計関数を作成することです。これは、これを最もクリーンに処理するためです(1秒未満の解像度などを処理します)。
実際、この質問は asktom.Oracle.com しばらく前に質問(および回答)されました(記事にはソースコードが含まれています)。
OracleでDATETIMEの差を秒単位で取得するには、複数の抽出を使用するヘアリー式よりも短く、速く、優れた方法があります。
これを試して、応答時間を秒単位で取得してください。
(sysdate + (endtime - starttime)*24*60*60 - sysdate)
また、タイムスタンプを減算するときに秒の小数部分を保持します。
詳細については、 http://kennethxu.blogspot.com/2009/04/converting-Oracle-interval-data-type-to.html を参照してください。
カスタムpl/sql関数にはかなりのパフォーマンスオーバーヘッドがあります これは重いクエリには適さない可能性があることに注意してください。
終了時刻と開始時刻が互いに1秒以内にない場合は、タイムスタンプを日付としてキャストし、日付演算を実行できます。
select avg(cast(endtime as date)-cast(starttime as date))*24*60*60
from timings;
OracleでINTERVAL DAY TO SECOND
をNUMBER
に明示的に変換する関数はないようです。 このドキュメント の最後にある表を参照してください。これは、そのような変換がないことを意味します。
他の情報源は、使用しているメソッドがINTERVAL DAY TO SECOND
データ型から数値を取得する唯一の方法であることを示しているようです。
この特定のケースで試すことができる他の唯一のことは、それらを減算する前に数値に変換することですが、それは2倍の数のextract
ionsを実行するため、さらに遅くなる可能性があります。
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;
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 |
まあ、これは本当に速くて汚い方法ですが、秒の差を別の列に保存し(レコードが変更された場合はトリガーを使用するか、手動で更新する必要があります)、その列を平均するのはどうですか?