繁体   English   中英

如何使Oracle将JSON与JSON而不是字符串进行比较

[英]How to make Oracle compare JSON as JSON, not as Strings

我正在努力查询包含来自外部系统的数据的JSON列。

考虑以下测试数据:

create table foo  
(  
  foo_id integer primary key,   
  payload clob,   
  constraint ensure_json CHECK (payload IS JSON)  
);  

insert into foo values (1, '{"data": {"k1": 1, "k2": "foo"}, "ref": {"id": 1, "type": "type1"}}');  
insert into foo values (2, '{"data": {"k1": 2, "k2": "bar"}, "ref": {"type": "type1", "id":1}}');  

我想检查“ ref”部分是否包含键/值对“ id:1”和“ type:type1”

我要比较的密钥是动态的,有效负载中的密钥也是动态的(正如我所说的那样,它是由外部源提供的)。 所以下面的查询:

select *  
from foo  
where json_query(payload, '$.ref') = '{"id":1,"type":"type1"}';  

将仅返回主foo_id = 1的行,而不返回另一行。 使用JSON_OBJECT()代替字符串文字不会改变任何内容。

我也尝试过: json_query(payload, '$.ref') = json_object('id' value 1, 'type' value 'type1')json_query(payload, '$.ref') = json_query('{"id":1,"type":"type1"}', '$')但仍然只能找到一行

根据JSON RFC( https://tools.ietf.org/html/rfc7159 ),键的顺序无关紧要。

因此,对象{"id": 1, "type": "type1"}{"type": "type1", "id": 1}是相同的,应视为相等,并且上面的查询应返回两行(至少这是我对JSON rfc的理解)

本质上,我正在寻找一种查询,其行为类似于以下Postgres查询(返回两行):

select *
from foo
where payload -> 'ref' = '{"id": 1, "type": "type1"}'::jsonb

假设payload定义为jsonb

我知道我可以使用以下方法解决此问题:

select *  
from foo  
where json_value(payload, '$.ref.type') = 'type1'  
   and json_value(payload, '$.ref.id') = '1';  

但是,这要求必须解析用于查询表的JSON对象,并将其拆分为元素。 对于像这样的简单示例,这在某种程度上是可以接受的,但是如果JSON更复杂(或嵌套在多个级别),这将成为一场噩梦。

在比较它们之前json_query(payload, '$.ref')有什么方法可以告诉Oracle使json_query(payload, '$.ref')返回的JSON对象“规范化”吗?

甚至更好:我可以告诉Oracle将它们作为真实的“对象”(=键/值对)而不是纯字符串进行比较吗?

理想的解决方案是我可以在Java代码中简单地准备一条语句,并可以插入任意JSON作为参数。

目前,我正在Oracle 12.2.0.1.0上对此进行测试,但是如果也有针对12.1的解决方案,那将是很好的。

当您足够幸运地升级到18c时,这很容易:使用JSON_equal。

这是一个新条件,完全可以满足您的要求:

select *
from   foo
where  json_equal (
  '{"type": "type1", "id":1}',
  json_query(payload, '$.ref')
);

FOO_ID   PAYLOAD                                                               
       1 {"data": {"k1": 1, "k2": "foo"}, "ref": {"id": 1, "type": "type1"}}   
       2 {"data": {"k1": 2, "k2": "bar"}, "ref": {"type": "type1", "id":1}} 

同时,您必须去找一些笨重的东西...

您可以使用JSON_table将JSON转换为关系格式:

select foo_id, id, type
from   foo, json_table (
  payload, '$' columns (
    nested path '$.ref[*]' columns (
      id path '$.id',
      type path '$.type'
    )
  )
);

FOO_ID   ID   TYPE    
       1 1    type1   
       2 1    type1  

然后对您的比较JSON执行相同操作。 并使用SQL设置差异进行比较。 有点麻烦...

或者在12.2上,您可以使用JSON_object以相同的顺序重建具有所有属性的对象:

with rws as (
  select foo_id, id, type
  from   foo, json_table (
    payload, '$' columns (
      nested path '$.ref[*]' columns (
        id path '$.id',
        type path '$.type'
      )
    )
  )
), j as (
  select foo_id, json_object (
           'id' value r.id, 'type' value r.type
         ) j
  from   rws r
)
  select * from j
  where  j.j = '{"id":"1","type":"type1"}';

FOO_ID   J                           
       1 {"id":"1","type":"type1"}   
       2 {"id":"1","type":"type1"}  

您可以做的另一件事是将数据存储为XML,而不是JSON。 原因是Oracle能够“感知” XML数据,并且实际上能够正确解释,操纵和解析它。 看来Oracle对JSON的实现尚未完全完成。 它只是存储为文本,并且可以通过检查约束来验证其语法,但是数据库内核不具备像XML那样真正理解数据的功能。 在将来的版本中可能会有所更改,但现在是一个缺点。

如今,在处理XML和JSON时,有许多辅助函数。 例如,APEX在APEX_JSON包中有一个非常有用的write_json过程,该过程可以将XMLTYPE作为直接输入来从中生成JSON。

还有一些将JSON转换为XML的过程,例如:

https://oracle-base.com/articles/misc/apex_json-package-generate-and-parse-json-documents-in-oracle#json-to-xml

如果匹配文字

 '{"id":1,"type":"type1"}'

生成后 ,此简单的解决方法将起作用。

select 
foo.*  
from foo  
where json_query(payload, '$.ref') in ('{"id":1,"type":"type1"}','{"type":"type1","id":1}');

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM