在 JSON 个数组上使用 Oracle 路径表达式

Using Oracle Path Expressions on JSON arrays

我正在尝试针对 Oracle JSON 数据结构编写一个带有路径表达式的简单查询,该查询将 return 学生姓名和他们的 CS220 老师的姓名(如果他们正在参加class).

JSON:

{
  'studentName': 'John Smith',
  'classes': [
    {
      'className': 'CS115',
      'teacherName': 'Sally Wilson'
    },
    {
      'className': 'CS220',
      'teacherName': 'Jason Wu'
    }
  ]
}

预期输出

Student Name      Professor
John Smith        Jason Wu
Jane Doe                             << Not taking CS220
Ajay Kumar        Robert Kroll

我希望写的查询:

Select 
    jsonfield.studentName,
    jsonfield.classes.<some path expression to find the CS220 professor here>
from mytable

我找到的唯一解决方案是将嵌套的 'classes' 投射到 table 并将其加入上面的查询以获取教授。我原以为 Oracle 的 json 路径实现能够在没有第二个查询的 overhead/complexity 的情况下解决这个问题。

在 12cR1 中,您可以执行以下操作:

select jt.studentname,
  max(case when jt.classname = 'CS220' then jt.teachername end) as teachername
from mytable mt
cross join json_table (
  mt.jsonfield,
  '$'
  columns (
    studentname varchar2(30) path '$.studentName',
    nested path '$.classes[*]' columns (
      classname varchar2(30) path '$.className',
      teachername varchar2(30) path '$.teacherName'
    )
  )
) jt
group by jt.studentname;

json_table() 将 JSON 拆分为关系列; nested path 表示每个 class(每个学生)获得一行,其中包含相关的 class 姓名和教师姓名。

然后 select 列表使用 case 表达式将任何其他 classes 的教师姓名更改为 null - 因此 John Smith 获得 CS220 和 Jason Wu 的一行,以及 CS220 和 Jason Wu 的一行CS115 和空。将其与 max() 合计会折叠这些内容,因此所有不相关的教师都将被忽略。

使用一些扩展示例数据:

create table mytable (jsonfield clob check (jsonfield is json));
insert into mytable a(jsonfield) values (q'#{
  'studentName': 'John Smith',
  'classes': [
    {
      'className': 'CS115',
      'teacherName': 'Sally Wilson'
    },
    {
      'className': 'CS220',
      'teacherName': 'Jason Wu'
    }
  ]
}#');
insert into mytable a(jsonfield) values (q'#{
  'studentName': 'Jane Doe',
  'classes': [
    {
      'className': 'CS115',
      'teacherName': 'Sally Wilson'
    }
  ]
}#');
insert into mytable a(jsonfield) values (q'#{
  'studentName': 'Ajay Kumar',
  'classes': [
    {
      'className': 'CS220',
      'teacherName': 'Robert Kroll'
    }
  ]
}#');

基本 json_table() 调用得到:

select jt.*,
  case when jt.classname = 'CS220' then jt.teachername end as adjusted_teachername
from mytable mt
cross join json_table (
  mt.jsonfield,
  '$'
  columns (
    studentname varchar2(30) path '$.studentName',
    nested path '$.classes[*]' columns (
      classname varchar2(30) path '$.className',
      teachername varchar2(30) path '$.teacherName'
    )
  )
) jt;

STUDENTNAME                    CLASSNAME                      TEACHERNAME                    ADJUSTED_TEACHERNAME         
------------------------------ ------------------------------ ------------------------------ ------------------------------
John Smith                     CS115                          Sally Wilson                                                 
John Smith                     CS220                          Jason Wu                       Jason Wu                      
Jane Doe                       CS115                          Sally Wilson                                                 
Ajay Kumar                     CS220                          Robert Kroll                   Robert Kroll                  

添加聚合步骤得到:

select jt.studentname,
  max(case when jt.classname = 'CS220' then jt.teachername end) as teachername
from mytable mt
cross join json_table (
  mt.jsonfield,
  '$'
  columns (
    studentname varchar2(30) path '$.studentName',
    nested path '$.classes[*]' columns (
      classname varchar2(30) path '$.className',
      teachername varchar2(30) path '$.teacherName'
    )
  )
) jt
group by jt.studentname;

STUDENTNAME                    TEACHERNAME                  
------------------------------ ------------------------------
John Smith                     Jason Wu                      
Jane Doe                                                     
Ajay Kumar                     Robert Kroll                  

在 12cR2 中,我 think thought 你也许可以做这样的事情,在 JSON 路径(这在 12cR1 中是不允许的):

select jt.*
from mytable mt
cross join json_table (
  mt.jsonfield,
  '$'
  columns (
    studentname varchar2(30) path '$.studentName',
    nested path '$.classes[*]?(@.className=="CS220")' columns (
      teachername varchar2(30) path '$.teacherName'
    )
  )
) jt;

...但是我没有合适的数据库来测试它。

...但结果是 "ORA-40553: path expression with predicates not supported in this operation" 和 "Only JSON_EXISTS supports predicates".