PostgreSql : Json 使用横向连接的数组到行
PostgreSql : Json Array to Rows using Lateral Join
我的 table 的详细信息字段中有两个以下 JSON 数组,需要评估我在另一个关系 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"]
}
我想为此 JSON 数组评估以下查询:
select quantities,
prices,
AVG(quantities/prices::float) as ratio
from my_table
where city = 'London'
group by quantities, prices;
我使用了以下查询 和许多类似的查询,包括横向连接 :
select q.*
from my_table mt
cross join json_array_elements_text(details -> 'quantities') as q
但是,当通过交叉连接将其他字段(价格和日期)添加到查询时,行数成倍增加。所以,我正在寻找一个新功能 Lateral Join
来使用,但无法正确应用。如何在 PostgreSQL 中使用 Lateral Join
获取我之前查询得到的结果?任何帮助将不胜感激。
更新:
这里是fiddle。如果我成功地将 json 数组值转换为行而不相乘(应返回 5 条记录),我就可以评估所需的结果。只需使用 lateral join 和 json_array_elements_text.
帮助我将 json 数组转换为行
这是你想要的吗?
-- 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'
见demo
由于数组中的顺序,您似乎需要 WITH ORDINALITY
和 LEFT JOIN LATERAL
s 来分别匹配数组的相应元素:
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;
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;
基本上,。只是一些改进和一些解释:
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
① 日期 字符串 默认根据区域设置和会话变量进行解释。使用 to_date()
.
以干净的方式进行
② SELECT
列表中的多个集合返回函数在 Postgres 10 之前的表现令人惊讶,如果结果行的数量不是全部相同的话。 (考虑升级。无论如何。)参见:
③ 在您的原始查询中 AVG(quantities/prices::float)
与 group by quantities, prices
组合没有任何意义。 quantities/prices
本身也没有。我按我认为合适的方式进行了修复,并投入 round()
来格式化输出。
④ 如果数量可以为 0 防止除以 0 NULLIF
:
, round(price / NULLIF(quantity, 0), 4) AS ratio
我的 table 的详细信息字段中有两个以下 JSON 数组,需要评估我在另一个关系 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"]
}
我想为此 JSON 数组评估以下查询:
select quantities,
prices,
AVG(quantities/prices::float) as ratio
from my_table
where city = 'London'
group by quantities, prices;
我使用了以下查询 和许多类似的查询,包括横向连接 :
select q.*
from my_table mt
cross join json_array_elements_text(details -> 'quantities') as q
但是,当通过交叉连接将其他字段(价格和日期)添加到查询时,行数成倍增加。所以,我正在寻找一个新功能 Lateral Join
来使用,但无法正确应用。如何在 PostgreSQL 中使用 Lateral Join
获取我之前查询得到的结果?任何帮助将不胜感激。
更新:
这里是fiddle。如果我成功地将 json 数组值转换为行而不相乘(应返回 5 条记录),我就可以评估所需的结果。只需使用 lateral join 和 json_array_elements_text.
帮助我将 json 数组转换为行这是你想要的吗?
-- 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'
见demo
由于数组中的顺序,您似乎需要 WITH ORDINALITY
和 LEFT JOIN LATERAL
s 来分别匹配数组的相应元素:
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;
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;
基本上,
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
① 日期 字符串 默认根据区域设置和会话变量进行解释。使用 to_date()
.
② SELECT
列表中的多个集合返回函数在 Postgres 10 之前的表现令人惊讶,如果结果行的数量不是全部相同的话。 (考虑升级。无论如何。)参见:
③ 在您的原始查询中 AVG(quantities/prices::float)
与 group by quantities, prices
组合没有任何意义。 quantities/prices
本身也没有。我按我认为合适的方式进行了修复,并投入 round()
来格式化输出。
④ 如果数量可以为 0 防止除以 0 NULLIF
:
, round(price / NULLIF(quantity, 0), 4) AS ratio