Nick
Nick

Reputation: 8493

Average interval between timestamps in an array

In a PostgreSQL 9.x database, I have a column which is an array of type timestamp. Each array has between 1..n timestamps.

I'm trying to extract the average interval between all elements in each array.

I understand using a window function on the source table might be the ideal way to tackle this but in this case I am trying to do it as an operation on the array.

I've looked at several other questions that are trying to calculate the moving average of another column etc or the avg (median date of a list of timestamps).

For example the average interval I'm looking for on an array with 3 elements like this:

'{"2012-10-09 17:04:05.710887"
 ,"2013-10-18 22:30:08.973749"
 ,"2014-10-22 22:18:18.885973"}'::timestamp[]

Would be:

-368d

Wondering if I need to unpack the array through a function?

Upvotes: 0

Views: 242

Answers (1)

Erwin Brandstetter
Erwin Brandstetter

Reputation: 656291

One way of many possible: unnest, join, avg in a lateral subquery:

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (
   SELECT avg(a2.ts - a1.ts) AS avg_intv
   FROM   unnest(t.arr) WITH ORDINALITY a1(ts, ord)
   JOIN   unnest(t.arr) WITH ORDINALITY a2(ts, ord) ON (a2.ord = a1.ord + 1)
   ) avg ON true;

db<>fiddle here

The [INNER] JOIN in the subquery produces exactly the set of combinations relevant for intervals between elements.

I get 371 days 14:37:06.587543, not '-368d', btw.

Related, with more explanation:

You can also only unnest once and use the window functions lead() or lag(), but you were trying to avoid window functions. And you need to make sure of the original order of elements in any case ...

(There is no array function you could use directly to get what you need - in case you were hoping for that.)

Alternative with CTE

Might be appealing to still unnest only once (even while avoiding window functions):

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (
   WITH   a AS (SELECT * FROM unnest(t.arr) WITH ORDINALITY a1(ts, ord))
   SELECT avg(a2.ts - a1.ts) AS avg_intv
   FROM   a a1
   JOIN   a a2 ON (a2.ord = a1.ord +1)
   ) avg ON true;

But I expect the added CTE overhead to cost more than unnesting twice. Mostly just demonstrating a WITH clause in a subquery.

Upvotes: 2

Related Questions