问题
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",
"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"]
}
I want to evaluate the following query for this JSON Array:
select quantities,
prices,
AVG(quantities/prices::float) as ratio
from my_table
where city = 'London'
group by quantities, prices;
I used the following query and many similar queries including lateral join:
select q.*
from my_table mt
cross join json_array_elements_text(details -> 'quantities') as q
But, when adding the other fields (prices and dates) to the query by cross join, the rows multiplied. So, I am looking for a new feature Lateral Join to use, but not able to apply properly. How can I obtain the result I obtained previous query by using Lateral Join in PostgreSQL? Any help would be appreciated.
Update:
Here is the fiddle. I can evaluate the desired result if I succeed to convert the json array values to rows without multiplying (5 records should be returned). Just help me to convert json array to row using lateral join and json_array_elements_text.
回答1:
Seems you need WITH ORDINALITY along with LEFT JOIN LATERALs 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
回答2:
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
回答3:
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;
回答4:
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:
- What is the expected behaviour for multiple set-returning functions in SELECT clause?
③ 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
来源:https://stackoverflow.com/questions/61709474/postgresql-json-array-to-rows-using-lateral-join