爲了應付無限遞歸問題,你需要使用一個recursive CTE每個錶行中在每個單獨的JSON元素進行操作:
WITH RECURSIVE
raw_json as (
SELECT
*
FROM
(VALUES
(1,
'{
"customerId": "12345",
"orders": [
{
"orderId": "54321",
"lineItems": [
{
"productId": "abc",
"qty": 3
},
{
"productId": "def",
"qty": 1
}
]
}
]
}'::json),
(2,
'{
"customerId": "678910",
"artibitraryLevel": {
"orders": [
{
"orderId": "55345",
"lineItems": [
{
"productId": "abc",
"qty": 3
},
{
"productId": "ghi",
"qty": 10
}
]
}
]
}
}'::json)
) a(id,sample_json)
),
json_recursive as (
SELECT
a.id,
b.k,
b.v,
b.json_type,
case when b.json_type = 'object' and not (b.v->>'customerId') is null then b.v->>'customerId' else a.customer_id end customer_id, --track any arbitrary id when iterating through json graph
case when b.json_type = 'object' and not (b.v->>'orderId') is null then b.v->>'orderId' else a.order_id end order_id,
case when b.json_type = 'object' and not (b.v->>'productId') is null then b.v->>'productId' else a.product_id end product_id
FROM
(
SELECT
id,
sample_json v,
case left(sample_json::text,1)
when '[' then 'array'
when '{' then 'object'
else 'scalar'
end json_type, --because choice of json accessor function depends on this, and for some reason postgres has no built in function to get this value
sample_json->>'customerId' customer_id,
sample_json->>'orderId' order_id,
sample_json->>'productId' product_id
FROM
raw_json
) a
CROSS JOIN LATERAL (
SELECT
b.k,
b.v,
case left(b.v::text,1)
when '[' then 'array'
when '{' then 'object'
else 'scalar'
end json_type
FROM
json_each(case json_type when 'object' then a.v else null end) b(k,v) --get key value pairs for individual elements if we are dealing with standard object
UNION ALL
SELECT
null::text k,
c.v,
case left(c.v::text,1)
when '[' then 'array'
when '{' then 'object'
else 'scalar'
end json_type
FROM
json_array_elements(case json_type when 'array' then a.v else null end) c(v) --if we have an array, just get the elements and use parent key
) b
UNION ALL --recursive term
SELECT
a.id,
b.k,
b.v,
b.json_type,
case when b.json_type = 'object' and not (b.v->>'customerId') is null then b.v->>'customerId' else a.customer_id end customer_id,
case when b.json_type = 'object' and not (b.v->>'orderId') is null then b.v->>'orderId' else a.order_id end order_id,
case when b.json_type = 'object' and not (b.v->>'productId') is null then b.v->>'productId' else a.product_id end product_id
FROM
json_recursive a
CROSS JOIN LATERAL (
SELECT
b.k,
b.v,
case left(b.v::text,1)
when '[' then 'array'
when '{' then 'object'
else 'scalar'
end json_type
FROM
json_each(case json_type when 'object' then a.v else null end) b(k,v)
UNION ALL
SELECT
a.k,
c.v,
case left(c.v::text,1)
when '[' then 'array'
when '{' then 'object'
else 'scalar'
end json_type
FROM
json_array_elements(case json_type when 'array' then a.v else null end) c(v)
) b
)
此後,您可以和「數量」的任意ID。 ..
SELECT
customer_id,
sum(v::text::integer)
FROM
json_recursive
WHERE
k = 'qty'
GROUP BY
customer_id
或者你可以得到 「LINEITEM」 對象和操縱它們,你的願望:
SELECT
*
FROM
json_recursive
WHERE
k = 'lineItems' and json_type = 'object'
至於索引,你可以在遞歸查詢改編成返回唯一的密鑰爲原始表的每一行中的每個JSON對象的功能,然後在你的JSON列創建函數索引:
SELECT
array_agg(DISTINCT k)
FROM
json_recursive
WHERE
not k is null