oracle sql中的JSON_TABLE未捕获嵌套的json数据

Posted

技术标签:

【中文标题】oracle sql中的JSON_TABLE未捕获嵌套的json数据【英文标题】:Nested json data not captured by JSON_TABLE in oracle sql 【发布时间】:2021-05-08 10:20:42 【问题描述】:

我正在使用 Oracle 12c(12.2) 读取表中的 json 数据。

SELECT        jt.name,
jt.employee_id, 
jt.company    
FROM JSON_TABLE ( BFILENAME ('DB_DIR', 'vv.json')

我在 json 输出中嵌套了数据。嵌套数据中的 key:value 以值开头 "past_work": "N.A" 记录。 对于它下面的其他许多记录,具有像

这样的实际值
"past_work": [ "company": "XXXXX",   "title": "XXXX"]

但是因为完成的第一条记录有值以及开始和结束括号 [],oracle 没有捕获下面的记录嵌套值。 知道如何捕获以下记录吗?

示例:如下所示的实际数据

  SELECT
      jt.company,
      jt.title
    FROM
      JSON_TABLE(
        '
           "employee_data": [
              "employee_id": "111",
               "past_work": "N/A"
             ,
              "employee_id": "222",
               "past_work": [
                 "company": "XXXXX", "title": "XXXX",
                 "company": "YYYYY", "title": "YYYY"
               ]
             ,
              "employee_id": "333",
               "past_work": [
                 "company": "XXXXX", "title": "XXXX",
                 "company": "YYYYY", "title": "YYYY"
                ]
             
           ]
         ',
        '$.past_work[*]'
          COLUMNS (
            company VARCHAR2(100) PATH '$.company',
            title   VARCHAR2(100) PATH '$.title'
          )
      )
        AS jt

现在,当我执行上述语句时,emplyee_id 333 及以下的公司值将变为 null。

谢谢

【问题讨论】:

这里有一些有用的材料,用于提供有用的示例来描述您的需求:***.com/help/minimal-reproducible-example 你想要什么结果??对于past_workN/A,结果的companytitle 列应该是什么? 您的 JSON 路径指向作为根元素的 past_work 元素,这不适合您的情况。要访问这些数组,您需要 $.employee_data[*].past_work[*] 感谢大家的意见。已要求产品团队提供正确格式的数据。 【参考方案1】:

如果 past_work 应该是过去的 (company, title) 对的数组,那么编码“无历史记录”的正确方法是不要使用像 "N/A" 这样的字符串值,而是应该使用空数组,正如我在下面的代码中所示。如果你按照自己的方式去做,你仍然可以提取数据,但它会异常混乱。如果使用 JSON,请正确使用。

另外,你说你想提取公司和头衔。就这些?这是没有意义的。相反,您可能希望提取每个员工的员工 ID 以及工作历史。在工作历史中,我添加了一个“for ordinality”列(以显示哪个公司是第一名,哪个是第二名,等等)如果您不需要它,请忽略它。

要访问嵌套列,您必须使用 columns 规范中的 nested 子句。

select employee_id, ord, company, title
from   json_table(
        '
           "employee_data": [
              "employee_id": "111",
               "past_work": [ ]
             ,
              "employee_id": "222",
               "past_work": [
                 "company": "XXXXX", "title": "XXXX",
                 "company": "YYYYY", "title": "YYYY"
               ]
             ,
              "employee_id": "333",
               "past_work": [
                 "company": "XXXXX", "title": "XXXX",
                 "company": "YYYYY", "title": "YYYY"
                ]
             
           ]
         ',   '$.employee_data[*]'
               columns ( 
                         employee_id varchar2(10) path '$.employee_id',
                           nested path '$.past_work[*]'
                               columns (
                                         ord     for ordinality,
                                         company varchar2(10) path '$.company',
                                         title   varchar2(10) path '$.title'
                                       )
                       )
       ) jt
order by employee_id, ord;

输出:

EMPLOYEE_ID ORD COMPANY TITLE
----------- --- ------- -----
111                          
222           1 XXXXX   XXXX 
222           2 YYYYY   YYYY 
333           1 XXXXX   XXXX 
333           2 YYYYY   YYYY 

【讨论】:

【参考方案2】:

首先,json sn-p 格式不正确,它必须被 包围才能被解析为 json 对象...

"past_work": [ "company": "XXXXX", "title": "XXXX"]

然后,您可以告诉 json 解析器您要从 past_work 元素中提取行...

JSON_TABLE(<yourJsonString>, '$.past_work[*]')

[*] 告诉解析器 past_work 是一个数组,并将该数组处理为 json 对象的行,而不是将整个数组作为单个 json 对象返回。

这给出了类似...

SELECT
  jt.company,
  jt.title
FROM
  JSON_TABLE(
    '
        "past_work": [
            "company": "XXXXX", "title": "XXXX",
            "company": "YYYYY", "title": "YYYY"
        ]
     ',
    '$.past_work[*]'
      COLUMNS (
        company VARCHAR2(100) PATH '$.company',
        title   VARCHAR2(100) PATH '$.title'
      )
  )
    AS jt

db<>fiddle demo

有关详细信息,我建议阅读文档:

https://docs.oracle.com/database/121/SQLRF/functions092.htm#SQLRF56973

编辑:更新的示例,几乎是从文档中复制和粘贴

请阅读文档!
SELECT
  jt.*
FROM
  JSON_TABLE(
    '
        "XX_data":[
          
            "employee_id": "E1",
            "full_name":   "E1  Admin",
            "past_work":   "N/A"
          ,
          
            "employee_id": "E2",
            "full_name":   "E2  Admin",
            "past_work": [
              "company": "E2 PW1 C", "title": "E2 PW1 T",
              "company": "E2 PW2 C", "title": "E2 PW2 T",
            ]
          ,
        ]
     ',
    '$.XX_data[*]'
      COLUMNS (
        employee_id VARCHAR2(100) PATH '$.employee_id',
        full_name   VARCHAR2(100) PATH '$.full_name',
        past_work   VARCHAR2(100) PATH '$.past_work',
        NESTED PATH '$.past_work[*]'
          COLUMNS (
            past_work_company VARCHAR2(100) PATH '$.company',
            past_work_title   VARCHAR2(100) PATH '$.title'
          )
      )
  )
    AS jt

Another db<>fiddle demo

【讨论】:

感谢您的回复。我的实际数据如下所示,并以 json 格式 "XXX_data":["employee_id": "XXX", "full_name": "XXXX Admin", "past_work": [ "company": "XXXXX", "title": "XXXX"] 正确形成,例如,我只是给了您嵌套数据。现在有很多记录,第一条记录有“past_work”:“N.A”没有值,没有[],但下面的记录有很多键:值。那么我如何获取该值。我的所有行都为空。谢谢 @NitinJoshi 编辑您的问题以包含一个完整且有用的示例 json 字符串,以及您希望从该字符串中获得的结果。评论不是放置这些详细信息的好地方,问题应包括所有描述您的需求所需的详细信息。【参考方案3】:

感谢大家的意见。已要求产品团队提供正确格式的数据。

【讨论】:

以上是关于oracle sql中的JSON_TABLE未捕获嵌套的json数据的主要内容,如果未能解决你的问题,请参考以下文章

JPA 标准 API 和 Oracle JSON_TABLE 函数

ORACLE JSON_TABLE 我需要从数组中获取 2 列

MariaDB 是不是具有 MYSQL/Oracle JSON_TABLE 的可扩展等效项

MySql 5.7对json_table()函数的一次变通替代

Oracle SQL Regexp 仅捕获字符串中的数字

如何从 oracle pl sql 中的包主体内的 select 语句中捕获特定列