我正在努力查询包含来自外部系统的数据的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}}');
Run Code Online (Sandbox Code Playgroud)
我想检查"ref"部分是否包含键/值对"id:1"和"type:type1"
我要比较的键是动态的,有效载荷中的键也是动态的(正如我所说的外部源提供的那样).所以以下查询:
select *
from foo
where json_query(payload, '$.ref') = '{"id":1,"type":"type1"}';
Run Code Online (Sandbox Code Playgroud)
只返回主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
Run Code Online (Sandbox Code Playgroud)
假设payload
定义为jsonb
我知道我可以解决这个问题,使用这样的方法:
select *
from foo
where json_value(payload, '$.ref.type') = 'type1'
and json_value(payload, '$.ref.id') = '1';
Run Code Online (Sandbox Code Playgroud)
但是,这要求必须解析用于查询表的JSON对象并将其拆分为其元素.对于一个简单的例子,这在某种程度上是可以接受的,但如果JSON更复杂(或嵌套在多个级别上),这就变成了一场噩梦.
有什么办法可以告诉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}}
Run Code Online (Sandbox Code Playgroud)
在此期间,你将不得不去找一些笨重的东西......
您可以使用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
Run Code Online (Sandbox Code Playgroud)
然后对你的比较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"}
Run Code Online (Sandbox Code Playgroud)