This is a discussion on temporal variants of generate_series() within the pgsql Hackers forums, part of the PostgreSQL category; --> I've written the following function definitions to extend generate_series to support some temporal types (timestamptz, date and time). Please ...
| |||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| I've written the following function definitions to extend generate_series to support some temporal types (timestamptz, date and time). Please include them if there's sufficient perceived need or value. -- timestamptz version CREATE OR REPLACE FUNCTION generate_series ( start_ts timestamptz , end_ts timestamptz , step interval ) RETURNS SETOF timestamptz AS $$ DECLARE current_ts timestamptz := start_ts; BEGIN IF start_ts < end_ts AND step > INTERVAL '0 seconds' THEN LOOP IF current_ts > end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; ELSIF end_ts < start_ts AND step < INTERVAL '0 seconds' THEN LOOP IF current_ts < end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE; -- date version CREATE OR REPLACE FUNCTION generate_series ( start_ts date , end_ts date , step interval ) RETURNS SETOF date AS $$ DECLARE current_ts date := start_ts; BEGIN IF start_ts < end_ts AND step > INTERVAL '0 seconds' THEN LOOP IF current_ts > end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; ELSIF end_ts < start_ts AND step < INTERVAL '0 seconds' THEN LOOP IF current_ts < end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE; -- time version CREATE OR REPLACE FUNCTION generate_series ( start_ts time , end_ts time , step interval ) RETURNS SETOF time AS $$ DECLARE current_ts time := start_ts; BEGIN IF step > INTERVAL '0 seconds' THEN LOOP -- handle wraparound first IF current_ts < end_ts THEN EXIT; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; LOOP IF current_ts > end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; ELSIF step < INTERVAL '0 seconds' THEN LOOP -- handle wraparound first IF current_ts > end_ts THEN EXIT; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; LOOP IF current_ts < end_ts THEN RETURN; END IF; RETURN NEXT current_ts; current_ts := current_ts + step; END LOOP; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE; |
| |||
| On Thu, 2007-04-12 at 14:56 -0700, Andrew Hammond wrote: > I've written the following function definitions to extend > generate_series to support some temporal types (timestamptz, date and > time). Please include them if there's sufficient perceived need or > value. I could see these being useful, but a PL/PgSQL implementation is not eligible for inclusion in the core backend (since PL/PgSQL is not enabled by default). -Neil ---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq |
| |||
| On Thu, Apr 12, 2007 at 02:56:24PM -0700, Andrew Hammond wrote: > I've written the following function definitions to extend > generate_series to support some temporal types (timestamptz, date and > time). Please include them if there's sufficient perceived need or > value. > > -- timestamptz version > CREATE OR REPLACE FUNCTION generate_series > ( start_ts timestamptz > , end_ts timestamptz > , step interval > ) RETURNS SETOF timestamptz > AS $$ > DECLARE > current_ts timestamptz := start_ts; > BEGIN > IF start_ts < end_ts AND step > INTERVAL '0 seconds' THEN > LOOP > IF current_ts > end_ts THEN > RETURN; > END IF; > RETURN NEXT current_ts; > current_ts := current_ts + step; > END LOOP; > ELSIF end_ts < start_ts AND step < INTERVAL '0 seconds' THEN > LOOP > IF current_ts < end_ts THEN > RETURN; > END IF; > RETURN NEXT current_ts; > current_ts := current_ts + step; > END LOOP; > END IF; > END; > $$ LANGUAGE plpgsql IMMUTABLE; Here's an SQL version without much in the way of bounds checking CREATE OR REPLACE FUNCTION generate_series ( start_ts timestamptz, end_ts timestamptz, step interval ) RETURNS SETOF timestamptz LANGUAGE sql AS $$ SELECT CASE WHEN $1 < $2 THEN $1 WHEN $1 > $2 THEN $2 END + s.i * $3 AS "generate_series" FROM generate_series( 0, floor( CASE WHEN $1 < $2 AND $3 > INTERVAL '0 seconds' THEN extract('epoch' FROM $2) - extract('epoch' FROM $1) WHEN $1 > $2 AND $3 < INTERVAL '0 seconds' THEN extract('epoch' FROM $1) - extract('epoch' FROM $2) END/extract('epoch' FROM $3) )::int8 ) AS s(i); $$; It should be straight-forward to make similar ones to those below. > CREATE OR REPLACE FUNCTION generate_series > ( start_ts date > , end_ts date > , step interval > ) RETURNS SETOF date > > -- time version > CREATE OR REPLACE FUNCTION generate_series > ( start_ts time > , end_ts time > , step interval > ) RETURNS SETOF time Cheers, D -- David Fetter <david@fetter.org> http://fetter.org/ phone: +1 415 235 3778 AIM: dfetter666 Skype: davidfetter Remember to vote! Consider donating to PostgreSQL: http://www.postgresql.org/about/donate ---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq |
| |||
| On Apr 28, 2007, at 8:00 PM, David Fetter wrote: > Here's an SQL version without much in the way of bounds checking > > CREATE OR REPLACE FUNCTION generate_series ( > start_ts timestamptz, > end_ts timestamptz, > step interval > ) RETURNS SETOF timestamptz > LANGUAGE sql > AS $$ > SELECT > CASE > WHEN $1 < $2 THEN > $1 > WHEN $1 > $2 THEN > $2 > END + s.i * $3 AS "generate_series" > FROM generate_series( > 0, > floor( > CASE > WHEN $1 < $2 AND $3 > INTERVAL '0 seconds' THEN > extract('epoch' FROM $2) - > extract('epoch' FROM $1) > WHEN $1 > $2 AND $3 < INTERVAL '0 seconds' THEN > extract('epoch' FROM $1) - > extract('epoch' FROM $2) > END/extract('epoch' FROM $3) > )::int8 > ) AS s(i); > $$; > > It should be straight-forward to make similar ones to those below. Are you sure the case statements are needed? It seems it would be better to just punt to the behavior of generate_series (esp. if generate_series eventually learns how to count backwards). -- Jim Nasby jim@nasby.net EnterpriseDB http://enterprisedb.com 512.569.9461 (cell) ---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives? http://archives.postgresql.org |
| |||
| Jim Nasby <decibel@decibel.org> writes: > Are you sure the case statements are needed? It seems it would be > better to just punt to the behavior of generate_series (esp. if > generate_series eventually learns how to count backwards). What's this "eventually"? regression=# select * from generate_series(10,1,-1); generate_series ----------------- 10 9 8 7 6 5 4 3 2 1 (10 rows) regards, tom lane ---------------------------(end of broadcast)--------------------------- TIP 2: Don't 'kill -9' the postmaster |
| ||||
| On Tue, May 01, 2007 at 05:08:45PM -0400, Tom Lane wrote: > Jim Nasby <decibel@decibel.org> writes: > > Are you sure the case statements are needed? It seems it would be > > better to just punt to the behavior of generate_series (esp. if > > generate_series eventually learns how to count backwards). > > What's this "eventually"? > > regression=# select * from generate_series(10,1,-1); > generate_series > ----------------- > 10 > 9 > 8 > 7 > 6 > 5 > 4 > 3 > 2 > 1 > (10 rows) > > regards, tom lane Good point. I believe the function below does the right thing. When given decreasing TIMESTAMPTZs and a negative interval, it will generate them going backward in time. When given increasing TIMESTAMPTZs and a positive interval, it will generate them going forward in time. Given a 0 interval, it errors out, although not with the same message as generate_series(1,1,0), and decreasing TIMESTAMPTZs and a positive interval or vice versa, it generates no rows. CREATE OR REPLACE FUNCTION generate_series ( start_ts timestamptz, end_ts timestamptz, step interval ) RETURNS SETOF timestamptz STRICT LANGUAGE sql AS $$ SELECT $1 + s.i * $3 AS "generate_series" FROM generate_series( CASE WHEN $1 <= $2 THEN 0 ELSE floor( ( extract('epoch' FROM $2) - extract('epoch' FROM $1) )/extract('epoch' FROM $3) )::int8 END, CASE WHEN $1 <= $2 THEN ceil( ( extract('epoch' FROM $2) - extract('epoch' FROM $1) )/extract('epoch' FROM $3) )::int8 ELSE 0 END, sign( extract('epoch' FROM $2) - extract('epoch' FROM $1) )::int8 ) AS s(i) ORDER BY s.i ASC ; $$; -- David Fetter <david@fetter.org> http://fetter.org/ phone: +1 415 235 3778 AIM: dfetter666 Skype: davidfetter Remember to vote! Consider donating to PostgreSQL: http://www.postgresql.org/about/donate ---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq |
| Thread Tools | |
| Display Modes | |
|
|