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

Oracle PL/SQL中JSON_TABLE提取PRODUCTS返回空数组问题

Oracle PL/SQL JSON_TABLE提取嵌套数组返回空数组问题解决

在使用Oracle PL/SQL存储过程处理JSON输入时,通过JSON_TABLE提取嵌套的Products字段时,始终返回空数组[],无法获取预期的产品列表。

原存储过程代码(泛化名)

CREATE OR REPLACE PACKAGE BODY "MY_PACKAGE" AS

    FUNCTION ProcessRequest(p_request_data IN CLOB, p_region_id IN VARCHAR2) RETURN CLOB IS
        v_result CLOB;
        v_geo    NUMBER;
        v_city   VARCHAR2(100);
    BEGIN
        SELECT json_value(p_request_data, '$.data.CITY')
        INTO v_city
        FROM dual;

        IF v_city = 'Town' THEN
            v_geo := 1;
        ELSE
            v_geo := 2;
        END IF;

        SELECT JSON_OBJECT(
                   'REQUEST_INFO' VALUE JSON_OBJECT(
                        'geo' VALUE v_geo,
                        'provider' VALUE p_region_id,
                        'nationalId' VALUE json_value(p_request_data, '$.data.NATIONAL_ID'),
                        'name' VALUE json_value(p_request_data, '$.data.FIRST_NAME'),
                        'family' VALUE json_value(p_request_data, '$.data.LAST_NAME'),
                        'phone' VALUE json_value(p_request_data, '$.data.PHONE'),
                        'requestNo' VALUE json_value(p_request_data, '$.data.REQUEST_NO'),
                        'subBranch' VALUE LPAD(json_value(p_request_data, '$.data.BRANCH_CODE'), 5, '0')
                    ),
                   'SUB_REQUEST' VALUE (
                       SELECT JSON_ARRAYAGG(
                                  JSON_OBJECT(
                                      'PHASE' VALUE PHASE,
                                      'LIC_STT_NAME' VALUE LIC_STT_NAME,
                                      'AMPER' VALUE TO_NUMBER(AMPER),
                                      'KILOWATT' VALUE TO_NUMBER(KILOWATT),
                                      'LIC_STT' VALUE LIC_STT,
                                      'PRODUCTS' VALUE (
                                          CASE 
                                              WHEN PRODUCTS IS NOT NULL THEN (
                                                  SELECT JSON_ARRAYAGG(
                                                             JSON_OBJECT(
                                                                 'PPEQ_COUNT' VALUE PPEQ_COUNT,
                                                                 'GOOD_ID' VALUE GOOD_ID
                                                             )
                                                         )
                                                  FROM JSON_TABLE(
                                                      TO_CLOB(PRODUCTS), 
                                                      '$[*]'
                                                      COLUMNS (
                                                          PPEQ_COUNT VARCHAR2(10) PATH '$.PPEQ_COUNT',
                                                          GOOD_ID VARCHAR2(20) PATH '$.GOOD_ID'
                                                      )
                                                  )
                                              )
                                              ELSE JSON_ARRAY()
                                          END
                                      )
                                  )
                              )
                       FROM JSON_TABLE(
                            p_request_data FORMAT JSON, 
                            '$.data.Radifs[*]'
                            COLUMNS (
                                PHASE VARCHAR2(10) PATH '$.PHASE',
                                LIC_STT_NAME VARCHAR2(100) PATH '$.LIC_STT_NAME',
                                AMPER VARCHAR2(10) PATH '$.AMPER',
                                KILOWATT VARCHAR2(10) PATH '$.KILOWATT',
                                LIC_STT VARCHAR2(10) PATH '$.LIC_STT',
                                PRODUCTS VARCHAR2(4000) PATH '$.Products'
                            )
                        )
                   )
               )
        INTO v_result
        FROM dual;

        RETURN v_result;
    END ProcessRequest;

END MY_PACKAGE;

调用示例

DECLARE
    v_request_data CLOB;
    v_region_id    VARCHAR2(100);
    v_result       CLOB;
BEGIN
    v_region_id := 'A2D93E00F04A4C198EEC6477E91A9DDE';

    v_request_data := '{
        "data": {
            "REQUEST_NO": "REQ123456",
            "BRANCH_CODE": "220",
            "Radifs": [
                {
                    "PHASE": "3",
                    "Products": [
                        {
                            "PPEQ_COUNT": "27",
                            "GOOD_ID": "999999"
                        }
                    ],
                    "LIC_STT_NAME": "Service Fee",
                    "AMPER": "25",
                    "KILOWATT": "0",
                    "LIC_STT": "89"
                }
            ],
            "CITY": "Village",
            "NATIONAL_ID": "1234567890",
            "LAST_NAME": "Doe",
            "FIRST_NAME": "John",
            "PHONE": "1234567890"
        },
        "message": null,
        "error": null,
        "status": true
    }';

    v_result := MY_PACKAGE.ProcessRequest(v_request_data, v_region_id);

    DBMS_OUTPUT.PUT_LINE(v_result);
END;

非预期输出

{
  "REQUEST_INFO": {
    "geo": 2,
    "provider": "A2D93E00F04A4C198EEC6477E91A9DDE",
    "nationalId": "1234567890",
    "name": "John",
    "family": "Doe",
    "phone": "1234567890",
    "requestNo": "REQ123456",
    "subBranch": "00220"
  },
  "SUB_REQUEST": [
    {
      "PHASE": "3",
      "LIC_STT_NAME": "Service Fee",
      "AMPER": 25,
      "KILOWATT": 0,
      "LIC_STT": "89",
      "PRODUCTS": []
    }
  ]
}

预期输出

{
  "REQUEST_INFO": {
    "geo": 2,
    "provider": "A2D93E00F04A4C198EEC6477E91A9DDE",
    "nationalId": "1234567890",
    "name": "John",
    "family": "Doe",
    "phone": "1234567890",
    "requestNo": "REQ123456",
    "subBranch": "00220"
  },
  "SUB_REQUEST": [
    {
      "PHASE": "3",
      "LIC_STT_NAME": "Service Fee",
      "AMPER": 25,
      "KILOWATT": 0,
      "LIC_STT": "89",
      "PRODUCTS": [
        {
          "PPEQ_COUNT": "27",
          "GOOD_ID": "999999"
        }
      ]
    }
  ]
}

问题原因

  1. 内层JSON_TABLE未指定FORMAT JSON:处理PRODUCTS字段时,传入的是字符串类型的JSON数据,但未声明FORMAT JSON,Oracle会将其当作普通文本解析,无法匹配JSON路径$[*],导致返回空结果,最终JSON_ARRAYAGG生成null,触发CASE分支返回空数组[]。
  2. 字段类型选择不当:将JSON数组存储到VARCHAR2列中,虽然示例数据长度足够,但长期来看存在截断风险,且不如JSON类型直观适配JSON数据。

修正方案

修改后的存储过程代码

CREATE OR REPLACE PACKAGE BODY "MY_PACKAGE" AS

    FUNCTION ProcessRequest(p_request_data IN CLOB, p_region_id IN VARCHAR2) RETURN CLOB IS
        v_result CLOB;
        v_geo    NUMBER;
        v_city   VARCHAR2(100);
    BEGIN
        SELECT json_value(p_request_data, '$.data.CITY')
        INTO v_city
        FROM dual;

        IF v_city = 'Town' THEN
            v_geo := 1;
        ELSE
            v_geo := 2;
        END IF;

        SELECT JSON_OBJECT(
                   'REQUEST_INFO' VALUE JSON_OBJECT(
                        'geo' VALUE v_geo,
                        'provider' VALUE p_region_id,
                        'nationalId' VALUE json_value(p_request_data, '$.data.NATIONAL_ID'),
                        'name' VALUE json_value(p_request_data, '$.data.FIRST_NAME'),
                        'family' VALUE json_value(p_request_data, '$.data.LAST_NAME'),
                        'phone' VALUE json_value(p_request_data, '$.data.PHONE'),
                        'requestNo' VALUE json_value(p_request_data, '$.data.REQUEST_NO'),
                        'subBranch' VALUE LPAD(json_value(p_request_data, '$.data.BRANCH_CODE'), 5, '0')
                    ),
                   'SUB_REQUEST' VALUE (
                       SELECT JSON_ARRAYAGG(
                                  JSON_OBJECT(
                                      'PHASE' VALUE PHASE,
                                      'LIC_STT_NAME' VALUE LIC_STT_NAME,
                                      'AMPER' VALUE TO_NUMBER(AMPER),
                                      'KILOWATT' VALUE TO_NUMBER(KILOWATT),
                                      'LIC_STT' VALUE LIC_STT,
                                      'PRODUCTS' VALUE (
                                          CASE 
                                              WHEN PRODUCTS IS NOT NULL THEN (
                                                  SELECT JSON_ARRAYAGG(
                                                             JSON_OBJECT(
                                                                 'PPEQ_COUNT' VALUE PPEQ_COUNT,
                                                                 'GOOD_ID' VALUE GOOD_ID
                                                             )
                                                         )
                                                  FROM JSON_TABLE(
                                                      PRODUCTS FORMAT JSON, -- 添加FORMAT JSON声明
                                                      '$[*]'
                                                      COLUMNS (
                                                          PPEQ_COUNT VARCHAR2(10) PATH '$.PPEQ_COUNT',
                                                          GOOD_ID VARCHAR2(20) PATH '$.GOOD_ID'
                                                      )
                                                  )
                                              )
                                              ELSE JSON_ARRAY()
                                          END
                                      )
                                  )
                              )
                       FROM JSON_TABLE(
                            p_request_data FORMAT JSON, 
                            '$.data.Radifs[*]'
                            COLUMNS (
                                PHASE VARCHAR2(10) PATH '$.PHASE',
                                LIC_STT_NAME VARCHAR2(100) PATH '$.LIC_STT_NAME',
                                AMPER VARCHAR2(10) PATH '$.AMPER',
                                KILOWATT VARCHAR2(10) PATH '$.KILOWATT',
                                LIC_STT VARCHAR2(10) PATH '$.LIC_STT',
                                PRODUCTS JSON PATH '$.Products' -- 将字段类型改为JSON
                            )
                        )
                   )
               )
        INTO v_result
        FROM dual;

        RETURN v_result;
    END ProcessRequest;

END MY_PACKAGE;

关键修改点

  1. 外层JSON_TABLE的PRODUCTS列类型改为JSON:直接存储JSON数组,避免字符串类型的转换和潜在截断问题。
  2. 内层JSON_TABLE添加FORMAT JSON:明确告知Oracle传入的是JSON数据,确保路径$[*]能正确匹配数组元素。
  3. 移除TO_CLOB转换:由于PRODUCTS已经是JSON类型,无需额外转换,直接传入JSON_TABLE即可。

替代兼容方案(针对不支持JSON类型的Oracle版本)

如果你的Oracle版本低于12cR2,不支持JSON类型,可以将PRODUCTS列改为CLOB类型,并保持内层FORMAT JSON声明:

-- 外层JSON_TABLE修改
PRODUCTS CLOB PATH '$.Products'

-- 内层JSON_TABLE修改
FROM JSON_TABLE(
    PRODUCTS FORMAT JSON,
    '$[*]'
    COLUMNS (...)
)

内容的提问来源于stack exchange,提问作者S.kh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 00:05:53