Postgres 9.4 eller nyere
Brug WITH ORDINALITY
for sæt-retur-funktioner:
Når en funktion i FROM
klausul er suffikset med WITH ORDINALITY
, en bigint
kolonne føjes til outputtet, som starter fra 1 og stiger med 1 for hver række af funktionens output. Dette er mest nyttigt i tilfælde af sæt returnerende funktioner såsom unnest()
.
I kombination med LATERAL
feature i pg 9.3+, og ifølge denne tråd om pgsql-hackere, kan ovenstående forespørgsel nu skrives som:
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 TRUE
bevarer alle rækker i den venstre tabel, selvom tabeludtrykket til højre ikke returnerer nogen rækker. Hvis det ikke er problematisk, kan du bruge dette ellers tilsvarende, mindre udførligt formular med en implicit CROSS JOIN LATERAL
:
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
Eller enklere, hvis den er baseret på en faktisk matrix (arr
er en matrixkolonne):
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
Eller endda med minimal syntaks:
SELECT id, a, ordinality
FROM tbl, unnest(arr) WITH ORDINALITY a;
a
er automatisk tabel og kolonne alias. Standardnavnet på den tilføjede ordinalitetskolonne er ordinality
. Men det er bedre (sikkert, renere) at tilføje eksplicitte kolonnealiasser og tabelkvalificerede kolonner.
Postgres 8.4 - 9.3
Med row_number() OVER (PARTITION BY id ORDER BY elem)
du får tal i henhold til sorteringsrækkefølgen, ikke ordenstallet for den oprindelige ordensposition i strengen.
Du kan simpelthen udelade ORDER BY
:
SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
Selvom dette normalt virker, og jeg aldrig har set det fejle i simple forespørgsler, hævder PostgreSQL intet vedrørende rækkefølgen af rækker uden ORDER BY
. Det virker tilfældigvis på grund af en implementeringsdetalje.
For at garantere ordenstal af elementer i den blank-separerede streng :
SELECT id, arr[nr] AS elem, nr
FROM (
SELECT *, generate_subscripts(arr, 1) AS nr
FROM (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t
) sub;
Eller enklere, hvis den er baseret på en faktisk matrix :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Relateret svar på dba.SE:
- Hvordan bevarer man den oprindelige rækkefølge af elementer i et ikke-indlejret array?
Postgres 8.1 - 8.4
Ingen af disse funktioner er tilgængelige endnu:RETURNS TABLE
, generate_subscripts()
, unnest()
, array_length()
. Men dette virker:
CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer)
RETURNS SETOF record
LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1
FROM generate_series(array_lower($1,1), array_upper($1,1)) i';
Bemærk især, at array-indekset kan afvige fra ordinalpositioner af elementer. Overvej denne demo med en udvidet funktion :
CREATE FUNCTION f_unnest_ord_idx(anyarray, OUT val anyelement, OUT ordinality int, OUT idx int)
RETURNS SETOF record
LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1, i
FROM generate_series(array_lower($1,1), array_upper($1,1)) i';
SELECT id, arr, (rec).*
FROM (
SELECT *, f_unnest_ord_idx(arr) AS rec
FROM (VALUES (1, '{a,b,c}'::text[]) -- short for: '[1:3]={a,b,c}'
, (2, '[5:7]={a,b,c}')
, (3, '[-9:-7]={a,b,c}')
) t(id, arr)
) sub;
id | arr | val | ordinality | idx
----+-----------------+-----+------------+-----
1 | {a,b,c} | a | 1 | 1
1 | {a,b,c} | b | 2 | 2
1 | {a,b,c} | c | 3 | 3
2 | [5:7]={a,b,c} | a | 1 | 5
2 | [5:7]={a,b,c} | b | 2 | 6
2 | [5:7]={a,b,c} | c | 3 | 7
3 | [-9:-7]={a,b,c} | a | 1 | -9
3 | [-9:-7]={a,b,c} | b | 2 | -8
3 | [-9:-7]={a,b,c} | c | 3 | -7
Sammenlign:
- Normaliser array-underskrifter for 1-dimensionelle array, så de starter med 1