2009-01-16 19 views
14

Oracle 10g'de, belirli işlemlerin ne kadar sürdüğünü gösteren zaman damgalarını içeren bir tablom var. İki zaman damgası alanı vardır: starttime ve bitiş zamanı. Bu zaman damgaları tarafından verilen sürelerin ortalamalarını bulmak istiyorum.Zaman aralıkları nasıl ortalama?

select avg(endtime-starttime) from timings; 

Ama olsun: Ben denemek

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

Ama gerçekten yavaş:

SQL Error: ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND

Bu çalışır.

Aralıkları saniye sayısına çevirmenin daha iyi bir yolu var mı?

DÜZENLEME: Gerçekten yavaşlatan şey, starttime'den önce biraz zaman geçirmemdi. Bazı sebeplerden dolayı bu hesaplama inanılmaz derecede yavaşladı. Temel sorunum, bunları sorgu kümesinden kaldırılarak çözüldü. Oracle'da NUMBER için INTERVAL DAY TO SECOND açık bir dönüşüm yapmak için herhangi bir fonksiyon var gibi

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; 

cevap

4

en temiz yolu Bunu yapmak için kendi toplama işlevini yazmaktır Çünkü bunu en temiz şekilde halledecektir (alt-saniye çözünürlüğü, vb.). Aslında, bu soru asktom.oracle.com numaralı telefondan bir süre önce (makale kaynak kodu içerir) sorulmuştur (ve cevaplandırılmıştır).

+1

[custom pl/sql işlevlerinin önemli bir performans yükü olduğunu] unutmayın (https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:60122715103602) ağır sorgular için uygun. – Vadzim

2

görünmüyor: Ben de sadece bu dönüşüm daha kolay yapmak için bir işlevi tanımladı. this document'un sonunda, böyle bir dönüşüm olmadığını gösteren tabloya bakın.

Diğer kaynaklar, kullandığınız yöntemin, INTERVAL DAY TO SECOND veri türünden bir sayı elde etmenin tek yolu olduğunu gösteriyor.

bunları çıkarılarak önce sayıya dönüştürmek olacağını bu özel durumda deneyebilirsiniz, ama bu iki kat daha fazla extract iyonları yapacağız çünkü, büyük olasılıkla olacak tek şey bile yavaş:

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; 
1

Bu gerçekten hızlı ve kirli bir yöntemdir, ancak saniyelerdeki farklılığı ayrı bir sütunda depolamaya (kayıt kullanmanız gerektiğinde bir tetikleyici kullanmanız mı, yoksa bunu manuel olarak güncellemeniz gerekir) ve bu sütunun üzerinde ortalaması nedir? senin endtime ve starttime birbirinden bir saniye içinde değilseniz

+1

Bunu yapmak istiyorsanız yapabilirsiniz sana bir sütunun bir tetikleyici veya manuel güncelleştirme kurtaran bir işlev tabanlı indeksi (FBI) kullanmak . Bir fbi, nerede-cümlede değil, aynı zamanda seçim maddelerinde de kullanılabilir. – tuinstoel

9

, tarihlere olarak damgaları döküm ve yapabileceği tarih aritmetik:

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

Bu, zaman damgalarında (birbirinin ikincisinin içinde olup olmadığına bakılmaksızın) kesirli saniye kaybeder. – MT0

16

Birden fazla özlü kıllı formülden daha kısa, daha hızlı ve daha güzel bir yol var.

Sadece saniyeler içinde yanıt süresini almak için bu deneyin: saniye

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

Ayrıca koruyan kesir kısmı damgaları çıkarılarak zaman. Bazı ayrıntılar için bkz. http://kennethxu.blogspot.com/2009/04/converting-oracle-interval-data-type-to.html Bkz.


Not ağır sorgular için uygun değildir olabilir custom pl/sql functions have significant performace overhead olduğu.

+1

şu ana kadarki en basit çözüm gibi görünüyor. Oracle bunun için normal bir işlev oluşturabilirse iyi olur. –

+1

Bu, "24 * 60 * 60 = 86400" ile aradaki farkın çoğunu oluşturacak ve daha sonra bir tarih olarak sonuç verecek ve kesirli saniyeleri kaybedecek olan tarihe ekleyecektir - böylece zaman damgaları bir mikrosaniye (veya 1/86400 saniyeden daha küçüktür) daha sonra doğruluk kaybeder. – MT0

+0

@ MT0, haklısınız. [TIMESTAMP (9)] (https://stackoverflow.com/questions/8987975/oracle-timestamp-data-type) için nanosaniye hassasiyeti ile elde edilebilir. (Sysdate + (end_ts - start_ts) * 24 * 60 * 60 * 1000000 - sysdate)/1000000.0'. – Vadzim

0

SQL Fiddle

Oracle 11g R2 Şema Kur:

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; 
/

Daha sonra özel toplama işlevini oluşturabilirsiniz:

özel toplanmasına gerçekleştirirken

kullanmak için bir tür oluşturma

Sorgu 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 

Results:

| AVERAGE(DIFF) | 
|---------------| 
|  0 2:0:0.0 | 
İlgili konular