I am having difficulties writing a Postgres function, as I am not familiar with it. I have multiple tables to import into Postgres with this format:
id | 1960 | 1961 | 1962 | 1963 | ...
____________________________________
1 23 45 87 99
2 12 31 ...
which I need to convert into this format:
id | year | value
_________________
1 1960 23
1 1961 45
1 1962 87
...
2 1960 12
2 1961 31
...
I would imagine the function too to read like this:
SELECT all-years FROM imported_table;
CREATE a new_table;
FROM min-year TO max-year LOOP
EXECUTE "INSERT INTO new_table (id, year, value) VALUES (id, year, value)";
END LOOP;
However, I'm having real trouble writing the nitty-gritty details for this. Would be easier for me to do that in PHP, but I am convinced that it's cleaner to do it directly in a Postgres-function.
The years (start and end) vary from table to table. And sometimes, I can even have years only for every fifth year or so ...
Parallel unnesting might be easier
A completely dynamic version requires dynamic SQL. Use a plpgsql function with
EXECUTE
:For Postgres 9.2 or older (before
LATERAL
was implemented):For Postgres 9.3 or later (with
LATERAL
):About
VARIADIC
:Call for arbitrary years:
Same, passing an actual array:
For a long list of sequential years:
For a long list with regular intervals (example for every 5 years):
Output as requested.
The function takes:
1. A valid table name - double-quoted if it's otherwise illegal (like
'"CaMeL"'
). Using the object identifier typeregclass
to assert correctness and defend against SQL injection. You may want to schema-qualify the tale name to be unambiguous (like'public."CaMeL"'
). More:2. Any list of numbers corresponding to (double-quoted) column names.
Or an actual array, prefixed with the keyword
VARIADIC
.The array of columns does not have to be sorted in any way, but table and columns must exist or an exception is raised.
Output is sorted by
id
andyear
(asinteger
). If you want years to be sorted according to the sort order of the input array, make it justORDER BY 1
. Sort order according to array is not strictly guaranteed, but works in the current implementation. More about that:Also works for
NULL
values.SQL Fiddle for both with examples.
References:
Is there something like a zip() function in PostgreSQL that combines two arrays?
Table name as a PostgreSQL function parameter
PostgreSQL generate_series() with SQL function as arguments
PostgreSQL 9.3
offers as neat JSON functions which can be used for such tasks without defining new functions or knowing a number of columns.http://sqlfiddle.com/#!15/1714b/13
The simplest way is a
union all
:A somewhat more sophisticated way would be:
You can put this into another table using
insert
orcreate table as
.