Reputation: 1006
Sample of my table
Table_A
id integer
arr varchar(20) []
Sample data
id. arr
1. {{'a', 'b', 'c'}, {'d', 'test1', 'sample1'}}
2. {{'sample2', sample3, sample4'}}
3. null
4. {{'sample6', 'sample7', 'test done'}}
I want to select to have the data as:
id. col1. col2 col3
1 'a' 'b'. 'c'
1. 'd'. 'test1'. 'sample1'
2. 'sample2' 'sample3'. 'sample4'
3. null null null
4. 'sample6'. 'sample7'. 'test done'
the data is guaranteed to have 5 elements in array if not null
. Sample data above is with 3 elements for convenience.
It's a fairly large table which will grow over time.
Right now I am doing it using unnest
and then row_number() over ()
and then joining them with id.
Is there any better way of doing it.
Upvotes: 2
Views: 4798
Reputation: 23766
unnest() for a regular multidimensional array has the problem, that it expands all elements of all dimensions, not only the first one. So, there is no way for these types to get this done.
A possible workaround is casting the array into a JSON array. For JSON arrays this is not a problem:
SELECT
id,
elems ->> 0 AS col1,
elems ->> 1 AS col2,
elems ->> 2 AS col3
FROM
table_a,
json_array_elements(array_to_json(arr)) elems
json_array_elements()
expands the first dimension of the array, so the subarrays are moved into their one row. Afterwards simple get their elemens by their indexes.
The query above eliminates the NULL rows because of its comma notation (which, in fact, is the shortcut for INNER JOIN LATERAL
in this case). This can be avoided using an LEFT OUTER JOIN LATERAL
instead:
SELECT
id,
elems ->> 0 AS col1,
elems ->> 1 AS col2,
elems ->> 2 AS col3
FROM
table_a
LEFT JOIN LATERAL
json_array_elements(array_to_json(arr)) elems ON TRUE
Upvotes: 3
Reputation: 45940
There is only one alternative - write procedural code in some PL language like Perl, Python or PLpgSQL. The code in Perl or Python will be more dynamic (you don't need special type), the code in PLpgSQL is easy to write. Probably procedural code should be faster for large arrays.
CREATE OR REPLACE FUNCTION unnest_to_columns3(a text[])
RETURNS TABLE(col1 text, col2 text, col3 text)
AS $$
DECLARE r text[];
BEGIN
FOREACH r SLICE 1 IN ARRAY a
LOOP
col1 := r[1]; col2 := r[2]; col3 := r[3];
RETURN NEXT;
END LOOP;
END
$$ LANGUAGE plpgsql;
postgres=# SELECT * FROM unnest_to_columns3('{{a,b,c},{Ahoj,Nazdar,Servus}}');
+------+--------+--------+
| col1 | col2 | col3 |
+------+--------+--------+
| a | b | c |
| Ahoj | Nazdar | Servus |
+------+--------+--------+
(2 rows)
Note: PostgreSQL has not a array of arrays - it has only multidimensional arrays.
It is much more practical work with arrays of records than multidimensional arrays. There is simple conversion between relations and arrays of this type.
create table foo(v1 text, v2 text, v3 text);
insert into foo values('ahoj','nazdar','servus');
insert into foo values('hi','greating',null);
postgres=# select array_agg(foo) from foo;
+-------------------------------------------+
| array_agg |
+-------------------------------------------+
| {"(ahoj,nazdar,servus)","(hi,greating,)"} |
+-------------------------------------------+
(1 row)
postgres=# select * from unnest('{"(ahoj,nazdar,servus)","(hi,greating,)"}'::foo[]);
+------+----------+--------+
| v1 | v2 | v3 |
+------+----------+--------+
| ahoj | nazdar | servus |
| hi | greating | ∅ |
+------+----------+--------+
(2 rows)
Important note - relation (table) is not a array, and a arrays is not a relation (table).
Upvotes: 2