1

I've got 2 columns in a database of type TIMESTAMP(6) WITH TIME ZONE. I've subtracted one from the other to get the time between the two timestamps.

select lastprocesseddate-importeddate 
from feedqueueitems 
where eventid = 2213283 
order by written desc;

How can I get an average of the list of time differences I have?

Here are a small sample of time differences:

+00 00:00:00.488871    
+00 00:00:00.464286  
+00 00:00:00.477107  
+00 00:00:00.507042  
+00 00:00:00.369144  
+00 00:00:00.488918  
+00 00:00:00.354797  
+00 00:00:00.378801  
+00 00:00:00.320040  
+00 00:00:00.361242  
+00 00:00:00.302327  
+00 00:00:00.331441  
+00 00:00:00.324065

EDIT: I also should have noted - I've tried the AVG function, and it just returns

ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND
00932. 00000 -  "inconsistent datatypes: expected %s got %s"
*Cause:    
*Action:
Error at Line: 3 Column: 29

EDIT2: Just to clarify the above snippet. Line 3 is my SQL query all on one line in the following format:

select AVG(lastprocesseddate-importeddate) from feedqueueitems where eventid = 2213283;

EDIT3: Massive thanks to Matt and Alex Poole. You've both helped massively and I appreciate you taking the time to help with this and to both consistently return with updated help in response to the feedback/further problems! Thanks guys!

Matt
  • 14,906
  • 27
  • 99
  • 149
James
  • 749
  • 1
  • 9
  • 22
  • possible duplicate of [How to average time intervals?](http://stackoverflow.com/questions/450581/how-to-average-time-intervals) – GolezTrol Apr 23 '15 at 10:17

3 Answers3

2

Use the AVG function

SELECT avg(cast(lastprocesseddate as date)-cast(importeddate as date))
FROM feedqueueitems 
WHERE eventid = 2213283 
ORDER BY written DESC;

On the Database with the +1 timezone for importeddate and lastprocesseddate is UTC

SELECT avg(cast(cast(lastprocesseddate as timestamp with time zone) at time zone '+01:00' as date)-cast(importeddate as date))
FROM feedqueueitems 
WHERE eventid = 2213283 
ORDER BY written DESC;
Matt
  • 14,906
  • 27
  • 99
  • 149
  • Hi, sorry I should have noted- I've tried AVG but it errors. I've edited my OP to reflect this. – James Apr 23 '15 at 10:15
  • Hi, yes it does not error anymore, I'm just trying to figure out the significance of what it returns now. I'm getting -0.0416605341348811498065229408512990602551 in one database, and 0.00001144103873988931460195828011919965942953 in another, and 0.00000986426767676767676767676767676767676767 in the third. So I'm having trouble distinguishing what this means! – James Apr 23 '15 at 10:41
  • When you say in one database, doe the different databases have different sets of data? – Matt Apr 23 '15 at 10:41
  • Yes. It's the same query executed on the different databases (but on the same set of data just with different times for lastimporteddate and importeddate - which is what I'm investigating) - I'm just analyzing the performance of these 3 environments and to assist me in this I'm trying to get the average time to process a message for each environment (this question!). The set of timestamps I'm getting the average of is the same format on each DB though. – James Apr 23 '15 at 10:45
  • @user2534905 - the values are fractions of a day; you can multiply by 86400 (seconds in a day) to see a more useful number - the first is -3599.47015 seconds, the second is .988505747 seconds, etc. But since the timestamps are cast to dates, you're losing the fractional seconds precision from all the values, before they are averaged. – Alex Poole Apr 23 '15 at 10:46
  • @AlexPoole How can you explain the average that returned a minus number? (-0.0416605341348811498065229408512990602551) – James Apr 23 '15 at 10:48
  • @user2534905 maybe some of the differences are negative. i.e. importeddate is greater than lastprocesseddate – Matt Apr 23 '15 at 10:49
  • @user2534905 that implies you have at least one set of values where the interval is negative, so the importdated is laster than the lastprocesseddate, which it seems you don't expect to see. Since it's almost exactly an hour, I'd suspect a time zone mistake? – Alex Poole Apr 23 '15 at 10:49
  • Ah good point about the timezone! On the database with the minus number, the importeddate is +01:00 and the lastprocesseddate is UTC, whereas in the two other databases, both columns are +01:00. Do you know any way around this? It's odd that there are no anomalies in the lastprocesseddate-importeddate query, but in the average there is? – James Apr 23 '15 at 10:56
  • @user2534905 updated answer with a possible answer for the DB where timezone is different – Matt Apr 23 '15 at 11:03
  • Works now and I can clearly compare them, cheer! Appreciate the time and effort you've taken :) – James Apr 23 '15 at 11:07
  • @user2534905 - it still won't accurate for sub-second gaps; for your example data, where the gaps are all less than a second, [the average will be zero](http://sqlfiddle.com/#!4/482b3/1) unless any happen to span a second boundary. That might be close enough for what you are doing though. – Alex Poole Apr 23 '15 at 11:34
2

You could extract the time components from each gap value, which is an interval data type, so you end up with a figure in seconds (including the fractional part), and then average those:

select avg(extract(second from gap)
    + extract(minute from gap) * 60
    + extract(hour from gap) * 60 * 60
    + extract(day from gap) * 60 * 60 * 24) as avg_gap
from (
  select lastprocesseddate-importeddate as gap
  from feedqueueitems
  where eventid = 2213283
);

A demo using a CTE to provide the interval values you showed:

with cte as (
  select interval '+00 00:00:00.488871' day to second as gap from dual
  union all select interval '+00 00:00:00.464286' day to second from dual
  union all select interval '+00 00:00:00.477107' day to second from dual
  union all select interval '+00 00:00:00.507042' day to second from dual
  union all select interval '+00 00:00:00.369144' day to second from dual
  union all select interval '+00 00:00:00.488918' day to second from dual
  union all select interval '+00 00:00:00.354797' day to second from dual 
  union all select interval '+00 00:00:00.378801' day to second from dual
  union all select interval '+00 00:00:00.320040' day to second from dual
  union all select interval '+00 00:00:00.361242' day to second from dual
  union all select interval '+00 00:00:00.302327' day to second from dual
  union all select interval '+00 00:00:00.331441' day to second from dual
  union all select interval '+00 00:00:00.324065' day to second from dual
)
select avg(extract(second from gap)
    + extract(minute from gap) * 60
    + extract(hour from gap) * 60 * 60
    + extract(day from gap) * 60 * 60 * 24) as avg_gap
from cte;

   AVG_GAP
----------
.397544692

Or if you wanted it as an interval:

select numtodsinterval(avg(extract(second from gap)
    + extract(minute from gap) * 60
    + extract(hour from gap) * 60 * 60
    + extract(day from gap) * 60 * 60 * 24), 'SECOND') as avg_gap
...

which gives

AVG_GAP            
--------------------
0 0:0:0.397544692   

SQL Fiddle with answer in seconds. (It doesn't seem to like displaying intervals at the moment, so can't demo that).

Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • Hi.Thanks a lot for the time and effort to write this response. I tried this method and I simply get returned AVG_GAP as (null) when running the query you provided at the start of your post. – James Apr 23 '15 at 10:51
  • @user2534905 - on a database where `select lastprocesseddate-importeddate from feedqueueitems where eventid = 2213283` returns data? – Alex Poole Apr 23 '15 at 10:57
  • Yeah ignore my comment - I was using the incorrect matchid. This is great! I'm getting (to 3 decimal places) - 0.909, 0.504 and 0.734. If I'm reading this correctly does this mean that the average time between imported and lastprocessed is 0.909 seconds, 0.504 seconds and 0.734 seconds? – James Apr 23 '15 at 11:04
  • @user2534905 - yes, though you might want to manually work out the value form a smaller sample manually to verify. This should report your negative value too. – Alex Poole Apr 23 '15 at 11:09
  • This doesn't report a negative value? – James Apr 23 '15 at 11:39
  • @user2534905 - the demo ([which does if I change one of the gaps to a large enough minus number](http://sqlfiddle.com/#!4/9450b/1)), or with your data that gets a minus value with Matt's answer? – Alex Poole Apr 23 '15 at 11:45
  • Hi sorry for the delay, with the dataset from the DB that has the inconsistent timezones (which is what is causing the negative results) I am getting 0.423 using your method whereas when using Matt's original method I get -0.041. It seems your method works fine for it. – James Apr 23 '15 at 13:34
  • @user2534905 - casting as date doesn't adjust the time zones, which could create false negatives. (Matt partially fixed that in an edit; you'd need to do 'at UTC; or some common time zone for both columns, not just one). [See this demo for how the dates and differences come out](http://sqlfiddle.com/#!4/ba27b/5) and how casting and the time zone affects things. Looks like you don't really have negative gaps, the casting to date is just making it look like that. Also notice the difference in precision when you use dates. – Alex Poole Apr 23 '15 at 14:02
  • Yeah, there is definitely not negative gaps I know that as a fact, it's just odd that Matts method returned a negative (his original method not the edited second method), yet your method without any tweaks seems to return accurate figures ("I'm getting (to 3 decimal places) - 0.909, 0.504 and 0.734. If I'm reading this correctly does this mean that the average time between imported and lastprocessed is 0.909 seconds, 0.504 seconds and 0.734 seconds?" – James Apr 23 '15 at 15:02
  • @user2534905 - but with the from/to timestamps in different time zones, casting straight to dates can make it look like there is a negative gap. If you have 09:00 +01:00 and 08:01 +00:00, they're really a minute apart; but cast as dates they become just 09:00 and 08:01, which is 59 minutes apart. Using intervals (like your original query, and my answer) takes time zones into account. – Alex Poole Apr 23 '15 at 15:07
0

This query should solve the issue.

WITH t AS 
    (SELECT 
        TIMESTAMP '2015-04-23 12:00:00.5 +02:00' AS lastprocesseddate,  
        TIMESTAMP '2015-04-23 12:05:10.21 UTC' AS importeddate 
    FROM dual)
SELECT 
    AVG(
        EXTRACT(SECOND FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate))
        + EXTRACT(MINUTE FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60
        + EXTRACT(HOUR FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60 * 60
        + EXTRACT(DAY FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60 * 60 * 24
    ) AS average_gap
FROM t;
Wernfried Domscheit
  • 54,457
  • 9
  • 76
  • 110