When I have a column with separated values, I can use the unnest() function:
myTable
id | elements
---+------------
1 |ab,cd,efg,hi
2 |jk,lm,no,pq
3 |rstuv,wxyz
select id, unnest(string_to_array(elements, ',')) AS elem
from myTable
id | elem
---+-----
1 | ab
1 | cd
1 | efg
1 | hi
2 | jk
...
How can I include element numbers? I.e.:
id | elem | nr
---+------+---
1 | ab | 1
1 | cd | 2
1 | efg | 3
1 | hi | 4
2 | jk | 1
...
I want the original position of each element in the source string. I’ve tried with window functions (row_number(), rank() etc.) but I always get 1. Maybe because they are in the same row of the source table?
I know it’s a bad table design. It’s not mine, I’m just trying to fix it.
Postgres 14 or later
Use
string_to_table()instead ofunnest(string_to_array())for a comma-separated string:SELECT t.id, a.elem, a.nr FROM tbl t LEFT JOIN LATERAL string_to_table(t.elements, ',') WITH ORDINALITY AS a(elem, nr) ON true;fiddle
Related:
Unnesting an actual array didn’t change since Postgres 9.4.
Postgres 9.4 or later
Use
WITH ORDINALITYfor set-returning functions:In combination with the
LATERALfeature in pg 9.3+, and according to this thread on pgsql-hackers, the above query can now be written as:SELECT t.id, a.elem, a.nr FROM tbl AS t LEFT JOIN LATERAL unnest(string_to_array(t.elements, ',')) WITH ORDINALITY AS a(elem, nr) ON true;LEFT JOIN ... ON truepreserves all rows from the left table, even if the table expression to the right returns no rows. See:If that’s of no concern you can use the otherwise equivalent, less verbose form with an implicit
CROSS JOIN LATERAL:Or simpler, based off an actual array (
arrbeing an array column):Or just go with default column names:
Or shorter, yet:
Or minimal syntax:
The last one returns all columns of
tbl, of course.ais automatically table and column alias (for the first column). The default name of the added ordinality column isordinality. But it’s clearer to add explicit column aliases and table-qualify columns.The original order of array elements is preserved this way. The manual for
unnest():Postgres 8.4 – 9.3
With
row_number() OVER (PARTITION BY id ORDER BY elem)you get numbers according to the sort order, not the ordinal number of the original ordinal position in the string.You can simply omit
ORDER BY:While this normally works and I have never seen it fail in simple queries, PostgreSQL asserts nothing concerning the order of rows without
ORDER BY. It happens to work due to an implementation detail.To guarantee ordinal numbers of elements in the blank-separated string:
Or simpler if based off an actual array:
Related answer on dba.SE:
Postgres 8.1 – 8.4
None of these features are available, yet:
RETURNS TABLE,generate_subscripts(),unnest(),array_length(). But this works:Note in particular, that the array index can differ from ordinal positions of elements. Consider this demo with an extended function:
Compare: