簡體   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