sql >> Base de Datos >  >> RDS >> PostgreSQL

PostgreSQL unnest () con número de elemento

Postgres 9.4 o posterior

Use WITH ORDINALITY para funciones de devolución de conjuntos:

Cuando una función en el FROM la cláusula tiene el sufijo WITH ORDINALITY , un bigint La columna se agrega a la salida que comienza en 1 y se incrementa en 1 por cada fila de la salida de la función. Esto es muy útil en el caso de funciones de devolución de conjuntos como unnest() .

En combinación con el LATERAL característica en pg 9.3+, y de acuerdo con este hilo en pgsql-hackers, la consulta anterior ahora se puede escribir como:

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 conserva todas las filas de la tabla de la izquierda, incluso si la expresión de la tabla de la derecha no devuelve filas. Si eso no le preocupa, puede usar este equivalente, menos detallado formulario con un implícito CROSS JOIN LATERAL :

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);

O más simple si se basa en una matriz real (arr siendo una columna de matriz):

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);

O incluso, con una sintaxis mínima:

SELECT id, a, ordinality
FROM   tbl, unnest(arr) WITH ORDINALITY a;

a es automáticamente tabla y alias de columna El nombre predeterminado de la columna de ordinalidad agregada es ordinality . Pero es mejor (más seguro, más limpio) agregar alias de columna explícitos y columnas de calificación de tabla.

Postgres 8.4 - 9.3

Con row_number() OVER (PARTITION BY id ORDER BY elem) obtiene números según el orden de clasificación, no el número ordinal de la posición ordinal original en la cadena.

Simplemente puede omitir ORDER BY :

SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM  (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;

Si bien esto normalmente funciona y nunca lo he visto fallar en consultas simples, PostgreSQL no afirma nada sobre el orden de las filas sin ORDER BY . Sucede que funciona debido a un detalle de implementación.

Para garantizar números ordinales de elementos en la cadena separada por espacios en blanco :

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;

O más simple si se basa en una matriz real :

SELECT id, arr[nr] AS elem, nr
FROM  (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;

Respuesta relacionada en dba.SE:

  • ¿Cómo preservar el orden original de los elementos en una matriz no anidada?

Postgres 8.1 - 8.4

Ninguna de estas funciones está disponible todavía:RETURNS TABLE , generate_subscripts() , unnest() , array_length() . Pero esto funciona:

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';

Tenga en cuenta, en particular, que el índice de la matriz puede diferir de las posiciones ordinales de los elementos. Considere esta demostración con una función extendida :

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

Comparar:

  • Normalice los subíndices de matriz para matriz unidimensional para que comiencen con 1