I have two following JSON Array in details field of my table and need to evaluate the query as I use in another relational table.
{
\"city\": \"London\",
select
quantity, price,
avg(quantity/price) as ratio
from my_table cross join lateral (
select
json_array_elements_text(details->'dates') as dates,
(json_array_elements_text(details->'quantities'))::numeric as quantity,
(json_array_elements_text(details->'prices'))::numeric as price) as data
where details->>'city' = 'London'
group by quantity, price;
Is this what you want ?
-- just simulate table:
with my_table(details) as(
values
('{
"city": "London",
"name": "Sainburry",
"quantities": [112, 145, 222, 122, 124],
"prices": [4, 4, 4, 0, 3],
"dates": ["13.05.2020", "14.05.2020", "15.05.2020", "16.05.2020", "17.05.2020"]
}'::json)
)
-- here is query:
select
my_table.details->>'city', u.quantities, u.prices
from my_table
JOIN LATERAL UNNEST(
ARRAY(SELECT json_array_elements_text(details->'quantities')) ,
ARRAY(SELECT json_array_elements_text(details->'prices'))
) u(quantities, prices) ON TRUE
WHERE
my_table.details->>'city' = 'London'
See demo
Seems you need WITH ORDINALITY
along with LEFT JOIN LATERAL
s to match the corresponding elements of the arrays due to the order in the arrays, respectively :
SELECT q.elm AS quantities, p.elm AS prices,
AVG(p.elm::float/q.elm::float) AS ratio
FROM my_table t0
LEFT JOIN LATERAL jsonb_array_elements(details -> 'quantities')
WITH ORDINALITY AS q(elm, i) ON TRUE
LEFT JOIN LATERAL jsonb_array_elements(details -> 'prices')
WITH ORDINALITY AS p(elm, i) ON q.i = p.i
LEFT JOIN LATERAL jsonb_array_elements(details -> 'dates')
WITH ORDINALITY AS d(elm, i) ON d.i = q.i
WHERE t0.details ->> 'city' = 'London'
GROUP BY q.elm, p.elm;
Demo
Basically, what Abelisto said. Just a couple of improvements and some explanation:
SELECT to_date(d, 'DD.MM.YYYY') AS date -- ①
, quantity, price
, round(price / quantity, 4) AS ratio -- ③, ④
FROM my_table
CROSS JOIN LATERAL ( -- ②
SELECT json_array_elements_text(details->'dates' ) AS d -- ①
, json_array_elements_text(details->'quantities')::int AS quantity -- ③
, json_array_elements_text(details->'prices' )::numeric AS price -- ③
) AS data
WHERE details->>'city' = 'London';
db<>fiddle here
① Date strings are interpreted depending on locale settings and session variables by default. Do it the clean way with to_date().
② Multiple set-returning functions in the SELECT
list behave in surprising ways up until Postgres 10 if the number of resulting rows is not exactly the same for all. (Consider upgrading. In any case.) See:
③ In your original query AVG(quantities/prices::float)
makes no sense in combination with group by quantities, prices
. Neither does quantities/prices
on its own. I fixed as I saw fit, and threw in round()
to format output.
④ If quantity can be 0 defend against division by 0 with NULLIF
:
, round(price / NULLIF(quantity, 0), 4) AS ratio