PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL unnest() avec le numéro d'élément

Postgres 9.4 ou version ultérieure

Utilisez WITH ORDINALITY pour les fonctions de retour d'ensemble :

Lorsqu'une fonction dans le FROM la clause est suffixée par WITH ORDINALITY , unbigint La colonne est ajoutée à la sortie qui commence à partir de 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 fonctions de retour d'ensemble telles que unnest() .

En combinaison avec le LATERAL fonctionnalité dans 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 à droite ne renvoie aucune ligne. Si cela ne vous concerne pas, vous pouvez utiliser cet équivalent autrement, moins verbeux forme avec un CROSS JOIN LATERAL implicite :

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 table et alias 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 qualifiées de table.

Postgres 8.4 - 9.3

Avec row_number() OVER (PARTITION BY id ORDER BY elem) vous obtenez des nombres selon l'ordre de tri, et non le 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 . Il se trouve que cela fonctionne en raison d'un détail de mise en œuvre.

Pour garantir les nombres ordinaux 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 :

  • Comment conserver l'ordre d'origine des éléments dans un tableau non imbriqué ?

Postgres 8.1 - 8.4

Aucune de ces fonctionnalités n'est disponible pour le moment :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[])  --  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

Comparez :

  • Normaliser les indices de tableau pour un tableau unidimensionnel afin qu'ils commencent par 1