Unnest array por un nivel

Quiero tomar una matriz de n dimensiones y devolver un conjunto que contiene filas de matrices de n-1 dimensiones. Por ejemplo, tome la matriz ARRAY[[1,2,3], [4,5,6], [7,8,9]] y devuelva un conjunto {1,2,3}, {4,5,6}, {7,8,9} . Usando unnest devuelve el conjunto 1,2,3,4,5,6,7,8,9 .

Intenté capturar la función Unnest de PostgreSQL 8.4, que parece que haría lo que estoy buscando:

 CREATE OR REPLACE FUNCTION tstng.unnest2(anyarray) RETURNS SETOF anyelement LANGUAGE plpgsql IMMUTABLE AS $$ BEGIN RETURN QUERY SELECT $1[i] FROM generate_series(array_lower($1,1), array_upper($1,1)) i; END; $$; 

Sin embargo, SELECT tstng.unnest2(ARRAY[[1,2,3], [4,5,6], [7,8,9]]); devuelve el conjunto , , (es decir: 3 filas nulas).

También encontré SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[0]; devuelve nulo, que creo que es la raíz de mi problema.

Explique

 SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[0] 

devuelve lo mismo que

 SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[17] 

que es NULL Cito los documentos sobre ese asunto :

De forma predeterminada, el valor del índice de límite inferior de las dimensiones de una matriz se establece en uno.

0 no tiene ningún significado especial aquí. Además, con matrices bidimensionales, necesita dos índices para obtener un elemento base. Me gusta esto:

 SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1][2] 

Resultado:

 2 

La primera parte de tu mensaje no está clara.

 SELECT array_dims(ARRAY[[1,2,3], [4,5,6], [7,8,9]]) 

Resultado:

 [1:3][1:3] 

Eso es dos dimensiones con 3 elementos (1 a 3) cada uno (9 elementos base).
Si quiere dimensiones n-1 , este es un resultado correcto:

 SELECT ARRAY (SELECT unnest('{{1,2,3}, {4,5,6}, {7,8,9}}'::int[])) 

Resultado:

 {1,2,3,4,5,6,7,8,9} 

Esa es una dimensión. unnest() siempre produce un elemento base por fila. No estoy seguro de qué resultado deseas exactamente. ¿Su ejemplo es simplemente otro conjunto de 2 dimensiones con un conjunto de llaves perdidas …?

 {1,2,3}, {4,5,6}, {7,8,9} 

Si quieres un trozo de la matriz , prueba esta notación:

 SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1:2] 

Resultado:

 {{1,2,3},{4,5,6}} 

O esto:

 SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[2:2][1:2] 

Resultado:

 {{4,5}} 

Para aplanar el resultado (obtener una matriz 1D):

  • Cómo seleccionar 1d array de 2d array postgresql

Lea más en el manual aquí .

Función

La prueba posterior reveló que esta función plpgsql es mucho más rápida. Requiere Postgres 9.1 o posterior:

 CREATE OR REPLACE FUNCTION unnest_2d_1d(ANYARRAY, OUT a ANYARRAY) RETURNS SETOF ANYARRAY AS $func$ BEGIN FOREACH a SLICE 1 IN ARRAY $1 LOOP RETURN NEXT; END LOOP; END $func$ LANGUAGE plpgsql IMMUTABLE; 

Ver:

  • ¿Cómo deshacerse de una matriz 2d en una matriz 1d rápidamente en PostgreSQL?

Esta es una versión mejorada y simplificada de la función que Lukas publicó :

 CREATE OR REPLACE FUNCTION unnest_2d_1d(anyarray) RETURNS SETOF anyarray AS $func$ SELECT array_agg($1[d1][d2]) FROM generate_subscripts($1,1) d1 , generate_subscripts($1,2) d2 GROUP BY d1 ORDER BY d1 $func$ LANGUAGE sql IMMUTABLE; 

Para las versiones de Postgres <8.4, array_agg() no está instalado por defecto. Créelo primero:

 CREATE AGGREGATE array_agg(anyelement) ( SFUNC=array_append, STYPE=anyarray, INITCOND='{}' ); 

Además, generate_subscripts() aún no ha nacido. Use en su lugar:

 ... FROM generate_series(array_lower($1,1), array_upper($1,1)) d1 , generate_series(array_lower($1,2), array_upper($1,2)) d2 ... 

Llamada:

 SELECT unnest_2d_1d(ARRAY[[1,2], [3,4], [5,6]]); 

Resultado

 {1,2} {3,4} {5,6} 

SQL Fiddle.

Las divisiones de una multidimensional se devuelven como matrices multidimensionales. Esta es una versión modificada de unnest que tomará una matriz bidimensional y devolverá un conjunto de matrices de 1 dimensión.

actualización : modificada para utilizar la función agregada incorporada array_agg que era la predeterminada a partir de 8.4. ( http://www.postgresql.org/docs/9.2/static/functions-aggregate.html )

Advertencias:

  • Solo funciona para arreglos bidimensionales (probablemente debería cambiar el nombre de la función para reflejar esa limitación).
  • Si está en 8.3 (y no puede actualizar), necesita tener definido el agregado array_accum y cambiar todas las referencias en las funciones siguientes de array_agg a array_accum. http://www.postgresql.org/docs/8.3/static/xaggr.html

código:

 CREATE OR REPLACE FUNCTION unnest_multidim(anyarray) RETURNS SETOF anyarray AS $BODY$ SELECT array_agg($1[series2.i][series2.x]) FROM (SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i FROM (SELECT generate_series(array_lower($1,1),array_upper($1,1)) as i) series1 ) series2 GROUP BY series2.i $BODY$ LANGUAGE sql IMMUTABLE; 

Resultado:

 select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]]); unnest_multidim ---------------------- {1,2,3} {4,5,6} {7,8,9} (3 rows) 

Ahora, digamos que por alguna razón quieres acceso fácil a solo una de estas matrices que se devuelve. La siguiente función agrega un parámetro de índice opcional que devolverá la matriz anidada del índice que proporcionó, o, si proporciona nulo, generará el conjunto completo de matrices “sin par”.

 CREATE OR REPLACE FUNCTION unnest_multidim(anyarray, integer) RETURNS SETOF anyarray AS $BODY$ SELECT array_agg($1[series2.i][series2.x]) FROM (SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i FROM (SELECT CASE WHEN $2 IS NULL THEN generate_series(array_lower($1,1),array_upper($1,1)) ELSE $2 END as i) series1 ) series2 GROUP BY series2.i $BODY$ LANGUAGE sql IMMUTABLE; 

Resultados:

 db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],2); unnest_multidim ----------------- {4,5,6} (1 row) db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],NULL); unnest_multidim ----------------- {1,2,3} {4,5,6} {7,8,9} (3 rows) 

Una advertencia: cuando se usa array_agg en postgres <9, la orden puede cambiar PostgreSQL array_agg order Si planea usar la matriz no parrada, digamos que para encontrar argmax, esto dañará sus datos.