Reputation: 703
I am working on a version of PostgreSQL 8.3 that does not support the variant of the generate_series()
functions for date/time series. I have this ugly workaround at the moment that calls the function with a third argument like:
select table_union('2012-12-01', '2013-02-20', 79)
I have to compute the last parameter manually to determine the number of days forgenerate_series()
.
What is the best way to modify this script so that I only need two arguments in the function call?
Is there a way to modify the below code to work the same way only given two arguments to the function like this?
select table_union('2012-12-01', '2013-02-20')
create or replace function table_union(date_from date, date_to date, numday int)
returns void language plpgsql as $$
declare
day_1 date;
_stop_ bigint := (date_from::date - date_to::date)::int;
begin
for day_1 in
select date_from + s.a as dates from generate_series(0, $3 ) as s(a)
loop
execute 'insert into dhcp.dhcp_map select * from dhcp.final_map_'|| trim( leading ' ' from to_char(extract(month from day_1), '09')) ||'_'||
trim( leading ' ' from to_char(extract(day from day_1), '09')); --to_char introduces a leadin space use trim to remove
end loop;
end; $$;
Update: I tried modifying my code after the great suggestions in the answer below but still have some errors:
create or replace function
table_union(date_from date, date_to date)
returns void language plpgsql
as $func$
declare day_1 date;
begin
for day_1 in select date_from
+ s.a as dates from generate_series(0, (date_to - date_from))
as s(a)
loop
execute
'insert into dhcp.dhcp_map select * from dhcp.final_map_'||
array_to_string(ARRAY(SELECT to_char(date_from + generate_series(0, (date_to - date_from)), 'MM_DD')) ) ;
end loop;
end;
$func$;
Upvotes: 1
Views: 1516
Reputation: 658112
CREATE OR REPLACE FUNCTION table_union(date_from date, date_to date)
RETURNS void LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE '
INSERT INTO dhcp.dhcp_map
SELECT * FROM dhcp.final_map_'
|| array_to_string(ARRAY(SELECT to_char(date_from
+ generate_series(0, (date_to - date_from)), 'MM_DD')), '
UNION ALL
SELECT * FROM dhcp.final_map_'
);
END
$func$;
Call:
SELECT table_union('2012-12-01', '2013-01-10');
Most important for performance: instead of one INSERT
statement per day I generate and execute a single INSERT
statement for everything. With RETURNS text
and RETURN
in place of EXECUTE
you can see the generated statement:
INSERT INTO dhcp.dhcp_map
SELECT * FROM dhcp.final_map_12_01
UNION ALL
SELECT * FROM dhcp.final_map_12_02
UNION ALL
SELECT * FROM dhcp.final_map_12_03
...
->sqlfiddle for Postgres 8.3.
As long as the amount of data to be inserted f9its into RAM, this is substantially faster.
If your INSERT
should be huge you may want stick with one INSERT
per day.
CREATE OR REPLACE FUNCTION table_union_huge(date_from date, date_to date)
RETURNS void LANGUAGE plpgsql AS
$func$
BEGIN
FOR i IN 0 .. (date_to - date_from)
LOOP
EXECUTE 'INSERT INTO dhcp.dhcp_map
SELECT * FROM dhcp.final_map_'|| (date_from + i)::text;
END LOOP;
END
$func$;
Remove redundant parameter $3
from function call. Replace with simple subtraction $2 - $1
. Returns an integer (difference in days) in Postgres.
Simplified generate_series()
call.
Replaced LOOP with ARRAY constructor and array_to_string()
to create a single statement.
Largely simplified string handling. Just use the pattern MM_DD
with to_char()
to extract your string from the date.
All Links to the manual of Postgres 8.3.
Upvotes: 3