问题
From the answer of the question How to use array_agg() for varchar[],
We can create a custom aggregate function to aggregate n-dimensional arrays in Postgres like:
CREATE AGGREGATE array_agg_mult (anyarray) (
SFUNC = array_cat
,STYPE = anyarray
,INITCOND = '{}'
);
A constrain is that the values have to share the same array extents and same length, handling empty values and different lengths doesn't work.
From the answer:
There is no way around that, the array type does not allow such a mismatch in Postgres. You could pad your arrays with NULL values so that all dimensions have matching extents.
I have the rows like
------ arrayfield
----- {1},
----- {},
----- {abc}
array_agg_mult(ARRAY[arrayfield]) AS customarray
I am expecting a aggregate resule like {{1},NULL,{abc}}
but it will throw
ERROR: cannot concatenate incompatible arrays
DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
Is there any way that we can add the padding values in the custom function ?
I found the issue is when the array length is different. {a},{null},{1} will aggregate, but {a,b},{},{1} will not.
So I need a query where I can add NULL elements to existing arrays.
One solution is to append two NULL always (2 is the max length going to be in that filed) array_cat(arr, ARRAY[NULL,NULL]) and trim the array to length 2:
{1} --> {1,NULL,NULL} --> {1,NULL}
{NULL} --> {NULL,NULL,NULL} --> {NULL,NULL}
{abc, def} --> {abc,def,NULL,NULL} --> {abc, def}
But I can't figure out the syntax.
回答1:
Using the custom aggregate function array_agg_mult() like defined in this related answer:
- Selecting data into a Postgres array
Your expected result is impossible:
{{1},NULL,{abc}}
Would have to be:
{{1},{NULL},{abc}}
Simple case with 0 or 1 array elements
For the simple case to just replace the empty array: You can achieve that with:
WITH t(arr) AS (
VALUES
('{1}'::text[])
,('{}')
,('{abc}')
)
SELECT array_agg_mult(ARRAY[CASE WHEN arr = '{}' THEN '{NULL}' ELSE arr END])
FROM t;
Dynamic padding for n elements
Using array_fill() to pad arrays with NULL elements up to the maximum length:
SELECT array_agg_mult(ARRAY[
arr || array_fill(NULL::text
, ARRAY[max_elem - COALESCE(array_length(arr, 1), 0)])
]) AS result
FROM t, (SELECT max(array_length(arr, 1)) AS max_elem FROM t) t1;
Still only works for 1-dimensional basic arrays.
Explain
- Subquery
t1computes the maximum length of the basic 1-dimensional array. COALESCE(array_length(arr, 1), 0)computes the length of the array in this row.COALESCEdefaults to0forNULL.- Generate padding array for the difference in length with
array_fill(). - Append that to
arrwith|| - Aggregate like above with
array_agg_mult().
SQL Fiddle. demonstrating all.
Output in SQL Fiddle is misleading, so I cast result to text there.
来源:https://stackoverflow.com/questions/26017890/pad-arrays-with-null-to-maximum-length-for-custom-aggregate-function