How to get rows in Postgres saving initial order in request? - sql

I have an array of ids:
[0, 1, 2, 3, 4]
and I want to get rows from Postgres table with these ids saving initial order in my array.
To get these rows I use select * from "table_name" WHERE id IN (ids).
After this query Postgres can return rows in this order
[4, 2, 1, 0, 5]
I know that I can change order to initial by myself, but may be there is a way to use another query to solve the problem?

Join against the unnested array rather than using an IN:
select t.*
from table_name t
join unnest(array[0,1,2,3,4]) with ordinality as a(id, idx) on a.id = t.id
order by a.idx;
The option with ordinality will return the index of each element in the array. And that index can then be used to sort the result.

Related

SQL Unnest- how to use correctly?

Say I have some data in a table, t.
id, arr
--, ---
1, [1,2,3]
2, [4,5,6]
SQL
SELECT AVG(n) FROM UNNEST(
SELECT arr FROM t AS n) AS avg_arr
This returns the error, 'Mismatched input 'SELECT'. Expecting <expression>.
What is the correct way to unnest an array and aggregate the unnested values?
unnest is normally used with a join and will expand the array into relation (i.e. for every element of array an row will be introduced). To calculate average you will need to group values back:
-- sample data
WITH dataset (id, arr) AS (
VALUES (1, array[1,2,3]),
(2, array[4,5,6])
)
--query
select id, avg(n)
from dataset
cross join unnest (arr) t(n)
group by id
Output:
id
_col1
1
2.0
2
5.0
But you also can use array functions. Depended on presto version either array_average:
select id, array_average(n)
from dataset
Or for older versions more cumbersome approach with manual aggregation via reduce:
select id, reduce(arr, 0.0, (s, x) -> s + x, s -> s) / cardinality(arr)
from dataset

Postgresql - Map array aggregates into a single array in a particular order

I have a PostgreSQL table containing a column of 1 dimensional array data. I wish to perform an aggregate query on this column, obtaining min/max/mean for each element of the array as well as the group count, returning the result as a 1 dimensional array. The array lengths in the table may vary, but I can be certain that in any grouping I perform, all arrays will be of the same length.
In a simple form, say my arrays are of length 2 and have readings for x and y, I want to return the result as
{Min(x), Max(x), Mean(x), Min(y), Max(y), Mean(y), Count()}
I am able to get a result in the form {Min(x), Min(y), Max(x), Max(y), Mean(x), Mean(y) Count()} but I can't get from there to my desired result.
Here's an example showing where I am so far (this time with arrays of length 3, but without the mean aggregation as there isnt one for arrays built in to pgSql):
(SQLFiddle here)
CREATE TABLE my_test(some_key numeric, event_data bigint[]);
INSERT INTO my_test(some_key, event_data) VALUES
(1, {11,12,13}),
(1, {5,6,7}),
(1, {-11,-12,-13});
SELECT MIN(event_data) || MAX(event_data) || COUNT(event_data) FROM my_test GROUP BY some_key;
The above gives me
{11,12,13,-11,-12,-13,3}
However, I don't know how to transform a result like the above into what I want, which is:
{11,-11,12,-12,13,-13,3}
What function should I use to transform the above?
Note that the aggregation functions above don't exactly match with those I am using to get min, max - I'm using the aggs_for_vecs extension to give me min, max and mean.
I would recommend using array operations and aggregation:
select x.some_key,
array_agg(u.val order by x.n, u.nn)
from (select t.some_key, ed.n, min(val) as minval, max(val) as maxval
from my_test t cross join lateral
unnest(t.event_data) with ordinality as ed(val, n)
group by t.some_key, ed.n
) x cross join lateral
unnest(array[x.minval, x.maxval]) with ordinality u(val, nn)
group by x.some_key;
Personally, I would prefer an array with three elements and the min/max as a record:
select x.some_key, array_agg((x.minval, x.maxval) order by x.n)
from (select t.some_key, ed.n, min(val) as minval, max(val) as maxval
from my_test t cross join lateral
unnest(t.event_data) with ordinality as ed(val, n)
group by t.some_key, ed.n
) x
group by x.some_key;
Here is a db<>fiddle.

Aggregate arrays element-wise in presto/athena

I have a table which has an array column. The size of the array is guaranteed to be same in all rows. Is it possible to do an element-wise aggregation on the arrays to create a new array?
For e.g. if my aggregation is the avg function then:
Array 1: [1,3,4,5]
Array 2: [3,5,6,1]
Output: [2,4,5,3]
I would want to write queries like these:
select
timestamp_column,
avg(array_column) as new_array
from
my_table
group by
timestamp_column
The array contains close to 200 elements, so I would prefer not to hardcode each element in the query :)
This can be done by combining 2 lesser known SQL constructs: UNNEST WITH ORDINALITY, and array_agg with ORDER BY.
The first step is to unpack the arrays into rows usingCROSS JOIN UNNEST(a) WITH ORDINALITY. For each element in each array, it will output a row containing the element value and the position of that element in the array.
Then you use a standardard GROUP BY on the ordinal, and sum the values.
Finally, you reassemble the sums back into an array using array_agg(value_sum ORDER BY ordinal). The critical part of this expression is the ORDER BY clause in the array_agg call. Without this the values would be an an arbitrary order.
Here is a full example:
WITH t(a) AS (VALUES array [1, 3, 4, 5], array [3, 5, 6, 1])
SELECT array_agg(value_sum ORDER BY ordinal)
FROM (
SELECT ordinal, sum(value) AS value_sum
from t
CROSS JOIN UNNEST(t.a) WITH ORDINALITY AS x(value, ordinal)
GROUP BY ordinal);

How to concatenate arrays grouped by another column in Presto?

Is this possible in SQL (preferably Presto):
I want to reshape this table:
id, array
1, ['something']
1, ['something else']
2, ['something']
To this table:
id, array
1, ['something', 'something else']
2, ['something']
In Presto you can use array_agg. Assuming that on input, all your arrays are single-element, this would look like this:
select id, array_agg(array[0])
from ...
group by id;
If, however, your input arrays are not necessarily single-element, you can combine this with flatten, like this:
select id, flatten(array_agg(array))
from ...
group by id;
If you want an array that shows the distinct items in the aggregated array then this should work:
select id, array_distinct(flatten(array_agg(array))) as array
from ...
group by id

Order by the IN value with jsonb array

I am trying to select records based on the order of array elements of another row:
SELECT *
FROM tester
WHERE id IN (
SELECT jsonb_array_elements(d->'fam')->>'id'
FROM tester
WHERE id='3'
)
I am aware of this solution.
The difference is that I don't know how to dynamically generate the "ordering" value. Is that possible?
The full fiddle is here.
I would like to see results based on the order found in the json data:
id name d
-- ----- --------
2 barb {"fam": [{"id": 1}, {"id": 3}]}
4 jaimie {"fam": [{"id": 3}, {"id": 2}, {"id": 1}]}
1 bob {"fam": [{"id": 3}, {"id": 2}, {"id": 4}]}
Use WITH ORDINALITY in a LATERAL join to preserve the original order of the array:
SELECT t.*
FROM tester t1
CROSS JOIN jsonb_array_elements(t1.d->'fam') WITH ORDINALITY fam(id, ord)
JOIN tester t ON t.id = fam.id->>'id'
WHERE t1.id = '3'
ORDER BY fam.ord;
SQL Fiddle.
Note a subtle difference: The IN construct in your original query not only removes original order, it also removes duplicates. My query keeps all IDs extracted from the array, duplicate or not, in original order.
The LATERAL keyword is optional noise for table functions. As is the AS keyword for table aliases. Would go here:
CROSS JOIN LATERAL jsonb_array_elements(d->'fam') WITH ORDINALITY AS fam(id, ord)
Related:
PostgreSQL unnest() with element number
How to get elements with a unique number from a json array in PostgreSQL?
Query for array elements inside JSON type
What is the difference between LATERAL and a subquery in PostgreSQL?