Postgres 9.4 ou version ultérieure
Utilisation WITH ORDINALITY
pour les fonctions de retour de set:
Lorsqu'une fonction de la FROM
clause est suffixée par WITH ORDINALITY
, une
bigint
colonne est ajoutée à la sortie qui commence à 1 et s'incrémente de 1 pour chaque ligne de la sortie de la fonction. Ceci est très utile dans le cas de set renvoyant des fonctions telles que unnest()
.
En combinaison avec la LATERAL
fonctionnalité de pg 9.3+ , et selon ce fil sur pgsql-hackers , la requête ci-dessus peut maintenant être écrite comme:
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
conserve toutes les lignes de la table de gauche, même si l'expression de table de droite ne renvoie aucune ligne. Si cela ne vous concerne pas, vous pouvez utiliser cette forme autrement équivalente, moins verbeuse avec un implicite CROSS JOIN LATERAL
:
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
Ou plus simple si basé sur un tableau réel ( arr
étant une colonne de tableau):
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
Ou même, avec une syntaxe minimale:
SELECT id, a, ordinality
FROM tbl, unnest(arr) WITH ORDINALITY a;
a
est automatiquement l' alias de table et de colonne. Le nom par défaut de la colonne d'ordinalité ajoutée est ordinality
. Mais il est préférable (plus sûr, plus propre) d'ajouter des alias de colonne explicites et des colonnes de qualification de table.
Postgres 8.4 - 9.3
Avec, row_number() OVER (PARTITION BY id ORDER BY elem)
vous obtenez des nombres en fonction de l'ordre de tri, pas du nombre ordinal de la position ordinale d'origine dans la chaîne.
Vous pouvez simplement omettre ORDER BY
:
SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
Bien que cela fonctionne normalement et que je ne l'ai jamais vu échouer dans des requêtes simples, PostgreSQL n'affirme rien concernant l'ordre des lignes sans ORDER BY
. Cela arrive à fonctionner en raison d'un détail de mise en œuvre.
Pour garantir le nombre ordinal d'éléments dans la chaîne séparée par des blancs :
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;
Ou plus simple si basé sur un tableau réel :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Réponse connexe sur dba.SE:
Postgres 8.1 - 8.4
Aucune de ces caractéristiques sont disponibles, mais: RETURNS TABLE
, generate_subscripts()
, unnest()
, array_length()
. Mais cela fonctionne:
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';
Notez en particulier que l'index du tableau peut différer des positions ordinales des éléments. Considérez cette démo avec une fonction étendue :
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[])
, (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
Comparer: