2009-01-16 22 views
14

ओरेकल 10 जी में मेरे पास एक सारणी है जिसमें टाइमस्टैम्प होते हैं जो दिखाते हैं कि कुछ संचालन कितने समय तक लेते थे। इसमें दो टाइमस्टैम्प फ़ील्ड हैं: स्टार्टटाइम और एंडटाइम। मैं इन टाइमस्टैम्प द्वारा दी गई अवधि के औसत ढूंढना चाहता हूं। मैं कोशिश:समय अंतराल औसत कैसे करें?

select avg(endtime-starttime) from timings; 

लेकिन मिलती है:

SQL Error: ORA-00932: inconsistent datatypes: expected NUMBER got 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

स्पष्ट तरीका यह करने के लिए अपने स्वयं के एकीकृत फ़ंक्शन लिखने के लिए है , क्योंकि यह इसे सबसे साफ तरीके से संभालेगा (उप-दूसरे संकल्प को संभालता है, आदि)।

असल में, इस प्रश्न से पूछा गया था (और उत्तर दिया गया) asktom.oracle.com पर थोड़ी देर पहले (लेख में स्रोत कोड शामिल है)।

+1

ध्यान दें कि [कस्टम पीएल/एसक्यूएल कार्यों में महत्वपूर्ण प्रदर्शन ओवरहेड है] (https://asktom.oracle.com/pls/apex/f?p=100:11TP:::P11_QUESTION_ID:60122715103602) जो हो सकता है भारी पूछताछ के लिए उपयुक्त है। – Vadzim

2

यह नहीं दिखता है Oracle में NUMBER को INTERVAL DAY TO SECOND की एक स्पष्ट रूपांतरण करने के लिए कोई समारोह नहीं है की तरह: मैं भी सिर्फ इस रूपांतरण आसान करने के लिए एक समारोह में परिभाषित किया। this document के अंत में तालिका देखें जो दर्शाती है कि ऐसा कोई रूपांतरण नहीं है।

अन्य स्रोत यह इंगित करते हैं कि आप जिस विधि का उपयोग कर रहे हैं वह INTERVAL DAY TO SECOND डेटाटाइप से एक नंबर प्राप्त करने का एकमात्र तरीका है।

केवल दूसरी बात आप इस विशेष मामले में कोशिश उन्हें घटाकर पहले नंबर करने के लिए कन्वर्ट करने के लिए किया जाएगा सकता है, लेकिन जब से है कि दो बार के रूप में कई 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; 
1

खैर, यह वास्तव में एक त्वरित और गंदे विधि है, लेकिन एक अलग कॉलम में सेकंड अंतर को संग्रहीत करने के बारे में क्या है (आपको एक ट्रिगर का उपयोग करने की आवश्यकता होगी या रिकॉर्ड में परिवर्तन होने पर मैन्युअल रूप से अपडेट करना होगा) और उस कॉलम पर औसत?

+1

यदि आप ऐसा करना चाहते हैं आप कर सकते हैं एक समारोह आधारित सूचकांक (एफबीआई) आप किसी ट्रिगर या स्तंभ के मैनुअल अद्यतन की बचत होती है कि का उपयोग । एक एफबीआई का इस्तेमाल कहां से किया जा सकता है लेकिन चयन-खंड में भी किया जा सकता है। – tuinstoel

9

अपने endtime और StartTime eachother के एक दूसरे के भीतर नहीं हैं, तो आप दिनांक के रूप में अपने timestamps डाली और क्या कर सकते हैं तिथि अंकगणित:

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

यह टाइमस्टैम्प में किसी भी आंशिक सेकंड को खो देगा (भले ही वे एक दूसरे के दूसरे के भीतर हों)। – MT0

16

कई निष्कर्षों के साथ उस बालों वाले सूत्र के मुकाबले एक छोटा, तेज़ और अच्छा तरीका है।

बस सेकंड में प्रतिक्रिया समय प्राप्त करने के लिए इस प्रयास करें: सेकंड के

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

यह भी बरकरार रखता है आंशिक हिस्सा जब timestamps घटाकर।

कुछ विवरणों के लिए http://kennethxu.blogspot.com/2009/04/converting-oracle-interval-data-type-to.html देखें।


कि नोट custom pl/sql functions have significant performace overhead कि भारी प्रश्नों के लिए उपयुक्त नहीं हो सकता।

+1

अब तक का सबसे सरल समाधान लगता है। अच्छा होगा अगर ओरेकल उसके लिए एक सामान्य कार्य कर सके। –

+1

यह '24 * 60 * 60 = 86400' द्वारा अंतराल अंतर को एकाधिक करेगा और फिर उस तारीख को जोड़ देगा जो परिणाम को तारीख के रूप में देगा और किसी भी अंशकालिक सेकंड को खो देगा - इसलिए यदि टाइमस्टैम्प माइक्रोक्रॉन्ड (या कुछ भी 1/86400 सेकंड से छोटा) तो यह सटीकता खो देगा। – MT0

+0

@ MT0, आप सही हैं। [TIMESTAMP (9)] के लिए नैनोसेकंद परिशुद्धता (https://stackoverflow.com/questions/8987975/oracle-timestamp-data-type) '(sysdate + (end_ts - start_ts) * 24 * 60 * 60 * के साथ हासिल की जा सकती है * 1000000 - sysdate)/1000000.0'। – Vadzim

0

SQL Fiddle

ओरेकल 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 

Results:

| AVERAGE(DIFF) | 
|---------------| 
|  0 2:0:0.0 | 
संबंधित मुद्दे