JSON数组元素展开及对象字段提取实践
场景介绍
某电商公司,订单信息表有三个字段:订单ID(order_id)、客户名称(customer_name)、订单详情(order_info)。其中订单详情 order_info以JSON格式存储了订单号、总金额、订单状态、商品列表(items数组,包含SKU、商品名称、数量、单价等信息)和配送信息(shipping对象,包含收货地址、联系电话、快递公司等)。
对订单信息表的查询如下:
1 2 3 4 5 | SELECT order_id, customer_name, order_info::json->'items' AS items FROM orders; |
查询结果如下:
1 2 3 4 5 6 7 8 9 | order_id | customer_name | items ------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------- ORD2026002 | user_2 | [{"sku":"SKU004","productName":"Monitor","quantity":1,"price":2599.00}] ORD2026001 | user_1 | [{"sku":"SKU001","productName":"Laptop","quantity":1,"price":8999.00},{"sku":"SKU002","productName":"Wireless Mouse","quantity":2,"price":199.00},{"sku":"SKU003","productName":" Mechanical Keyboard","quantity":1,"price":699.00}] ORD2026003 | user_3 | [{"sku":"SKU005","productName":"Headphone","quantity":1,"price":599.00},{"sku":"SKU006","productName":"Power Bank","quantity":1,"price":299.00}] (3 rows) |
每行数据对应一个订单,items字段返回完整的JSON数组字符串,但无法直接对数组内的商品进行统计和筛选。一个订单会包含多个商品记录,公司需要知道每个订单购买了哪些商品、各商品的购买数量和单价,以及配送方式等信息,以便进行销售统计、库存管理和物流分析。
场景需求分析:
- 统计各商品的销售数量和销售额。
- 分析不同快递公司的配送订单量。
- 查询特定状态订单的商品明细。
- 计算每个订单的商品种类数和总金额。
数据特点:
- 订单与商品为一对多关系,一个订单可包含多个商品。
- 商品信息存储在JSON数组中,需要展开为多行才能进行统计分析。
- 配送信息为嵌套JSON对象,需要逐层提取字段值。
JSON数组元素转为多行示例
- 创建示例订单表orders,并插入数据。
1 2 3 4 5 6 7 8 9 10 11 12 13
CREATE TABLE orders ( order_id VARCHAR(50), customer_name VARCHAR(100), order_info TEXT ); INSERT INTO orders VALUES ('ORD2026001', 'user_1', '{"orderNo":"PO20260901001","totalAmount":15999.00,"status":"completed","items":[{"sku":"SKU001","productName":"Laptop","quantity":1,"price":8999.00},{"sku":"SKU002","productName":"Wireless Mouse","quantity":2,"price":199.00},{"sku":"SKU003","productName":"Mechanical Keyboard","quantity":1,"price":699.00}],"shipping":{"address":"Building 1, Mountain Street, H District","phone":"6666666","express":"DHL Express"}}'), ('ORD2026002', 'user_2', '{"orderNo":"PO20260901002","totalAmount":2599.00,"status":"shipped","items":[{"sku":"SKU004","productName":"Monitor","quantity":1,"price":2599.00}],"shipping":{"address":"Building 2, Garden Street, K District","phone":"7777777","express":"UPS Logistics"}}'), ('ORD2026003', 'user_3', '{"orderNo":"PO20260901003","totalAmount":899.00,"status":"pending","items":[{"sku":"SKU005","productName":"Headphone","quantity":1,"price":599.00},{"sku":"SKU006","productName":"Power Bank","quantity":1,"price":299.00}],"shipping":{"address":"Building 3, Central Street, S District","phone":"8888888","express":"GLS Express"}}');
- JSON数组元素转多行(商品列表展开)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
SELECT order_id, customer_name, item->>'sku' AS sku, item->>'productName' AS product_name, (item->>'quantity')::INT AS quantity, (item->>'price')::NUMERIC(10,2) AS price FROM ( SELECT order_id, customer_name, json_array_elements(order_info::json->'items') AS item FROM orders ) t;
返回结果:
1 2 3 4 5 6 7 8 9
order_id | customer_name | sku | product_name | quantity | price ------------+---------------+--------+---------------------+----------+--------- ORD2026001 | user_1 | SKU001 | Laptop | 1 | 8999.00 ORD2026001 | user_1 | SKU002 | Wireless Mouse | 2 | 199.00 ORD2026001 | user_1 | SKU003 | Mechanical Keyboard | 1 | 699.00 ORD2026003 | user_3 | SKU005 | Headphone | 1 | 599.00 ORD2026003 | user_3 | SKU006 | Power Bank | 1 | 299.00 ORD2026002 | user_2 | SKU004 | Monitor | 1 | 2599.00 (6 rows)
- 提取嵌套对象字段(配送信息)。
1 2 3 4 5 6 7 8 9 10
SELECT order_id, customer_name, order_info::json->>'orderNo' AS order_no, order_info::json->>'totalAmount' AS total_amount, order_info::json->>'status' AS status, order_info::json->'shipping'->>'address' AS shipping_address, order_info::json->'shipping'->>'phone' AS shipping_phone, order_info::json->'shipping'->>'express' AS express_company FROM orders;
返回结果:1 2 3 4 5 6
order_id | customer_name | order_no | total_amount | status | shipping_address | shipping_phone | express_company ------------+---------------+---------------+--------------+-----------+-----------------------------------------+----------------+----------------- ORD2026001 | user_1 | PO20260901001 | 15999.00 | completed | Building 1, Mountain Street, H District | 6666666 | DHL Express ORD2026003 | user_3 | PO20260901003 | 899.00 | pending | Building 3, Central Street, S District | 8888888 | GLS Express ORD2026002 | user_2 | PO20260901002 | 2599.00 | shipped | Building 2, Garden Street, K District | 7777777 | UPS Logistics (3 rows)
- 综合查询(订单+商品+配送)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
SELECT o.order_id, o.customer_name, o.order_info::json->>'orderNo' AS order_no, t.item->>'productName' AS product_name, (t.item->>'quantity')::INT AS quantity, (t.item->>'price')::NUMERIC(10,2) AS unit_price, o.order_info::json->'shipping'->>'express' AS express_company, o.order_info::json->>'status' AS status FROM orders o, ( SELECT order_id, json_array_elements(order_info::json->'items') AS item FROM orders ) t WHERE o.order_id = t.order_id ORDER BY o.order_id;
返回结果:1 2 3 4 5 6 7 8 9
order_id | customer_name | order_no | product_name | quantity | unit_price | express_company | status ------------+---------------+---------------+---------------------+----------+------------+-----------------+----------- ORD2026001 | user_1 | PO20260901001 | Laptop | 1 | 8999.00 | DHL Express | completed ORD2026001 | user_1 | PO20260901001 | Wireless Mouse | 2 | 199.00 | DHL Express | completed ORD2026001 | user_1 | PO20260901001 | Mechanical Keyboard | 1 | 699.00 | DHL Express | completed ORD2026002 | user_2 | PO20260901002 | Monitor | 1 | 2599.00 | UPS Logistics | shipped ORD2026003 | user_3 | PO20260901003 | Headphone | 1 | 599.00 | GLS Express | pending ORD2026003 | user_3 | PO20260901003 | Power Bank | 1 | 299.00 | GLS Express | pending (6 rows)
JSON操作符和函数参考
| 操作符 | 说明 | 示例 | ||
|---|---|---|---|---|
| -> | 获取JSON对象字段(返回JSON类型)。 |
| ||
| ->> | 获取JSON对象字段(返回文本类型)。 |
| ||
| #> | 按路径获取JSON值。 |
| ||
| #>> | 按路径获取文本值。 |
| ||
| json_array_elements() | JSON数组展开为多行。 |
| ||
| json_array_length() | 获取数组长度。 |
|
操作注意事项
- TEXT类型字段需先用“::json”转换为JSON类型才能使用JSON操作符。
- “->”返回JSON类型,可继续链式操作;“->>”返回文本类型,用于最终取值。
- 数值类型字段建议用“::INT”或“::NUMERIC”转换后进行计算。
- 如果JSON数组可能为空,可使用LEFT JOIN避免丢失主表数据,空数组对应字段显示为空。