You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中JSON嵌套对象与数组的WHERE过滤查询问题

PostgreSQL JSON字段查询问题:处理混合对象/数组的items字段

表结构与数据

首先,我们有一张存储JSON数据的表:

CREATE TABLE js.orders (
    id serial NOT NULL PRIMARY KEY,
    info json NOT NULL
);

表中包含6行数据:

SELECT * FROM js.orders;

查询结果:

id |                                                info                                 
----+----------------------------------------------------------------------------------------------------
  1 | { "customer": "Kapil", "items": {"product": "Heineken","qty": 6}}
  2 | { "customer": "Satyen", "items": {"product": "Heineken","qty": 18}}
  3 | { "customer": "Rekha", "items": {"product": "Carlsberg","qty": 24}}
  4 | { "customer": "Madhuri", "items": {"product": "Kalyani","qty": 12}}
  5 | { "customer": "Srinivas", "items": {"product": "Kingfisher Strong","qty": 12}}
  6 | { "customer": "Saina", "items": [{"product": "Bira91","qty": 6},{"product": "Kalyani","qty": 6} ]}
(6 rows)

问题场景

查询购买"Heineken"的客户时,语句返回正确结果:

SELECT info ->> 'customer' AS customer 
FROM js.orders 
WHERE info -> 'items' ->> 'product' = 'Heineken';

返回:

customer 
----------
 Kapil
 Satyen
(2 rows)

但查询购买"Kalyani"的客户时,本该返回Madhuri和Saina两行,却只返回一行:

SELECT info ->> 'customer' AS customer 
FROM js.orders 
WHERE info -> 'items' ->> 'product' = 'Kalyani';

返回:

customer     
-----------------
 Madhuri
(1 row)

原因是Saina的items是数组而非单个对象,原查询仅能处理items为单个对象的情况。

解决方案

方案1:修改查询适配混合结构

通过json_typeof判断items的类型,将单个对象转换为数组后统一展开,再筛选目标产品:

SELECT info ->> 'customer' AS customer
FROM js.orders
WHERE EXISTS (
    SELECT 1
    FROM json_array_elements(
        CASE json_typeof(info -> 'items')
            WHEN 'array' THEN info -> 'items'
            ELSE json_build_array(info -> 'items')
        END
    ) AS item
    WHERE item ->> 'product' = 'Kalyani'
);

这个查询用EXISTS判断该行是否存在匹配的产品,无需去重,会返回Madhuri和Saina两行正确结果。

另一种写法(使用LATERAL JOIN+DISTINCT):

SELECT DISTINCT info ->> 'customer' AS customer
FROM js.orders
LEFT JOIN LATERAL (
    SELECT * FROM json_array_elements(
        CASE json_typeof(info -> 'items')
            WHEN 'array' THEN info -> 'items'
            ELSE json_build_array(info -> 'items')
        END
    ) AS item
) AS items ON true
WHERE item ->> 'product' = 'Kalyani';

方案2:统一数据结构(推荐)

如果业务允许,建议将所有items字段统一为数组格式,避免混合结构带来的查询复杂度:

-- 将单个对象的items转换为数组
UPDATE js.orders
SET info = jsonb_set(
    info::jsonb,
    '{items}',
    jsonb_build_array(info -> 'items'),
    false
)
WHERE json_typeof(info -> 'items') = 'object';

修改后,所有items都是数组,查询可简化为:

SELECT DISTINCT info ->> 'customer' AS customer
FROM js.orders,
     json_array_elements(info -> 'items') AS item
WHERE item ->> 'product' = 'Kalyani';

内容的提问来源于stack exchange,提问作者Calcutta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:47:39