oracle中對JSON數(shù)據(jù)處理的詳細(xì)指南
背景
在公司接手的項(xiàng)目碰到這么一個(gè)表,里面有一列數(shù)據(jù)保存的是JSON字符串。如果說這一列是一些配置數(shù)據(jù),不需要應(yīng)用到篩選和過濾,只是跟著主鍵去獲得的數(shù)據(jù),那你這么做并沒有什么不妥。但事實(shí)是,這一列需要檢索和過濾數(shù)據(jù),這**居然是遍歷全表,再把json數(shù)據(jù)轉(zhuǎn)換成對象去遍歷匹配。我服了,你咋這么能呢?喜歡這樣玩為什么當(dāng)初不用MongoDB?查了一下文檔,知道Oracle 在12C之后有對JSON數(shù)據(jù)的操作方法,網(wǎng)上關(guān)于這部分的文章非常非常非常少(畢竟沒幾個(gè)人會把oracle當(dāng)成MongoDB這么地**是吧),就整理一下,方便自己以后查看。
入門探究
有一說一這官方文檔非常地硬,啃了好久都啃不動(dòng),而且很多理想當(dāng)然的用法都不行。Oracle中關(guān)于JSON的操作函數(shù)有JSON_ARRAY、JSON_EXISTS、JSON_VALUES、JSON_TABLE、JSON_QUERY,其中JSON_ARRAY是用來構(gòu)建JSON數(shù)組的,而不是用來查找結(jié)果的。在文中也會小篇幅簡單說一下用法。
JSON_EXISTS
JSON_EXISTS是一個(gè)SQL函數(shù),用于判斷是否存在符合條件的JSON數(shù)據(jù)。它可以在WHERE子句中使用,用于篩選JSON列中符合特定條件的數(shù)據(jù)。
官方給出的語法是這樣的:
JSON_EXISTS(json_column, json_path_expression, [returning_clause])
其中,json_column是指要查詢的JSON列,json_path_expression是指JSON路徑表達(dá)式,用于指定要查找的JSON數(shù)據(jù)的位置和條件。
JSON路徑表達(dá)式中可以使用各種操作符和函數(shù),以及通配符和其他特殊字符。例如:
$: 表示根元素.: 表示當(dāng)前元素[*]: 表示匹配數(shù)組中的所有元素[]: 表示過濾數(shù)組中符合條件的元素@: 表示當(dāng)前元素的屬性
在json_path_expression中,可以使用一些函數(shù),如JSON_VALUE、JSON_QUERY、JSON_TABLE等來處理JSON數(shù)據(jù)。例如:
- JSON_VALUE(json_column, '$.name'): 返回json_column列中name字段的值
- JSON_QUERY(json_column, '$.name'): 返回json_column列中address字段的值
- JSON_TABLE(json_column, '$.hobbies[*]' COLUMNS (phone_number VARCHAR2(20) PATH '$')): 返回json_column列中phones數(shù)組中的所有元素的phone_number字段的值
返回值:
如果符合條件的JSON數(shù)據(jù)存在,則返回TRUE;否則返回FALSE。如果使用returning_clause子句,則返回符合條件的JSON數(shù)據(jù)。
例如:
SELECT * FROM my_table WHERE JSON_EXISTS(json_data, '$.name');
這個(gè)例子查詢my_table表中json_data列中是否存在name字段的值。如果存在,返回TRUE,否則返回FALSE。
JSON_ARRAY
JSON_ARRAY 是一個(gè) Oracle SQL 函數(shù),用于創(chuàng)建一個(gè) JSON 數(shù)組。下面是使用 JSON_ARRAY 的一些示例:
語法:
JSON_ARRAY(value1 [, value2] [, value3] ...);
其中,value1, value2, value3 等是一個(gè)或多個(gè)要添加到 JSON 數(shù)組的值。這些值可以是任何有效的 SQL 值,例如字符串、數(shù)字、日期、布爾值等。如果沒有指定任何值,JSON_ARRAY 將生成一個(gè)空的 JSON 數(shù)組。
創(chuàng)建一個(gè)包含兩個(gè)字符串值的 JSON 數(shù)組
SELECT JSON_ARRAY('apple', 'orange') FROM dual;輸出結(jié)果:
["apple", "orange"]
創(chuàng)建一個(gè)包含兩個(gè)數(shù)字值的 JSON 數(shù)組
SELECT JSON_ARRAY(10, 20) FROM dual;
輸出結(jié)果:
[10, 20]
創(chuàng)建一個(gè)包含多個(gè)元素的 JSON 數(shù)組
SELECT JSON_ARRAY('apple', 10, TRUE) FROM dual;輸出結(jié)果:
["apple", 10, true]
在上述示例中,JSON_ARRAY 函數(shù)接受一個(gè)或多個(gè)參數(shù),并將它們作為一個(gè) JSON 數(shù)組返回。你可以在 JSON_ARRAY 中使用不同類型的參數(shù),例如字符串,數(shù)字和布爾值,它們都將被轉(zhuǎn)換為相應(yīng)的 JSON 類型。
JSON_VALUE
JSON_VALUE函數(shù)用于提取JSON文檔中的值。它的語法如下:
JSON_VALUE(json_document, path_expression [RETURNING datatype])
其中:
json_document是包含JSON文檔的字符串或BLOB列。path_expression是用于指定要提取的值的路徑表達(dá)式。datatype是可選的,用于指定返回值的數(shù)據(jù)類型。
以下是一些示例:
提取JSON文檔中的單個(gè)值:
SELECT JSON_VALUE('{"name": "John", "age": 30}', '$.name') as name FROM dual;輸出:
NAME----John
提取JSON文檔中的數(shù)組:
SELECT JSON_VALUE('{"fruits": ["apple", "banana", "orange"]}', '$.fruits') as fruits FROM dual;輸出:
FRUITS
-----------------------
["apple", "banana", "orange"]
提取JSON文檔中的數(shù)組元素:
SELECT JSON_VALUE('{"fruits": ["apple", "banana", "orange"]}', '$.fruits[0]') as first_fruit FROM dual;輸出:
FIRST_FRUIT
-----------
apple
指定返回值的數(shù)據(jù)類型:
SELECT JSON_VALUE('{"price": 9.99}', '$.price' RETURNING NUMBER) as price FROM dual;輸出:
PRICE
-----
9.99
在使用JSON_VALUE函數(shù)時(shí),需要注意以下幾點(diǎn):
- 如果路徑表達(dá)式不匹配JSON文檔中的任何內(nèi)容,則返回NULL。
- 如果未指定返回值的數(shù)據(jù)類型,則返回的值將是一個(gè)字符串。
- JSON_VALUE函數(shù)還有一個(gè)類似的兄弟函數(shù)JSON_QUERY,不同的是,它返回JSON對象或數(shù)組,而不是標(biāo)量值。
JSON_QUERY
JSON_QUERY函數(shù)用于從JSON文檔中查詢數(shù)據(jù),返回一個(gè)JSON對象或數(shù)組。它的語法如下:
JSON_QUERY(json_document, path_expression [RETURNING datatype])
其中:
json_document是包含JSON文檔的字符串或BLOB列。path_expression是用于指定要查詢的值的路徑表達(dá)式。datatype是可選的,用于指定返回值的數(shù)據(jù)類型。
以下是一些示例:
查詢JSON文檔中的單個(gè)值:
SELECT JSON_QUERY('{"name": "John", "age": 30}', '$.name') as name FROM dual;輸出:
NAME
----
null
查詢JSON文檔中的數(shù)組:
SELECT JSON_QUERY('{"fruits": ["apple", "banana", "orange"]}', '$.fruits') as fruits FROM dual;輸出:
FRUITS
-----------------------
["apple", "banana", "orange"]
查詢JSON文檔中的數(shù)組元素:
SELECT JSON_QUERY('{"fruits": ["apple", "banana", "orange"]}', '$.fruits[0]') as first_fruit FROM dual;輸出:
FIRST_FRUIT
-----------
null
因?yàn)槭菃沃?,json_query無法展示
在使用JSON_QUERY函數(shù)時(shí),需要注意以下幾點(diǎn):
- 如果路徑表達(dá)式不匹配JSON文檔中的任何內(nèi)容,則返回NULL。
- 如果未指定返回值的數(shù)據(jù)類型,則返回的值將是一個(gè)字符串。
- JSON_QUERY函數(shù)還有一個(gè)類似的兄弟函數(shù)JSON_VALUE,不同的是,它返回標(biāo)量值,而不是JSON對象或數(shù)組。
總之,JSON_QUERY函數(shù)在處理JSON文檔時(shí)非常有用,可以輕松地查詢和提取需要的數(shù)據(jù)。
JSON_TABLE
JSON_TABLE函數(shù)可以將JSON數(shù)據(jù)轉(zhuǎn)換為表格形式。以下是JSON_TABLE的用法:
語法:
JSON_TABLE(json, path COLUMNS (column1 expr1 [, column2 expr2]...))
參數(shù)說明:
json:要轉(zhuǎn)換的JSON數(shù)據(jù)。path:要提取的JSON元素的路徑。COLUMNS:指定要轉(zhuǎn)換的列及其表達(dá)式。column1 expr1 [, column2 expr2]...:指定要轉(zhuǎn)換的列及其表達(dá)式。
示例:
假設(shè)我們有以下JSON數(shù)據(jù):
{
"employees": [
{ "name": "John", "age": 30, "gender": "male" },
{ "name": "Jane", "age": 25, "gender": "female" },
{ "name": "Bob", "age": 35, "gender": "male" }
]
}我們可以使用以下查詢將其轉(zhuǎn)換為表格形式:
SELECT name, age, gender
FROM JSON_TABLE('{
"employees": [
{ "name": "John", "age": 30, "gender": "male" },
{ "name": "Jane", "age": 25, "gender": "female" },
{ "name": "Bob", "age": 35, "gender": "male" }
]
}', '$.employees[*]' COLUMNS (
name VARCHAR2(50) PATH '$.name',
age NUMBER PATH '$.age',
gender VARCHAR2(10) PATH '$.gender'
));輸出結(jié)果:
NAME AGE GENDER
------ ---- ------
John 30 male
Jane 25 female
Bob 35 male
在上面的例子中,$.employees[*]指定要處理的JSON元素路徑,name, age, 和 gender 列使用 PATH 關(guān)鍵字指定表達(dá)式的路徑。
進(jìn)階實(shí)戰(zhàn)篇
我舉個(gè)實(shí)際的例子,我有一列,對應(yīng)的一列的數(shù)據(jù)大概是這樣的:
{
"name": "cxk", // string
"sex": "female", // stirng
"hobbies": ["sing", "dance", "rap", "basketball"], // 普通數(shù)組
"company": {"name": "unknow", "staffNum": "unknow"}, // 對象屬性
"fans": [ // 對象數(shù)組
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}1. JSON_QUERY與JSON_VALUE的區(qū)別
看了前面的介紹,可能有些同學(xué)對這個(gè)完全不能理解,有什么區(qū)別?
查詢單值
select JSON_QUERY
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.name'
) as json_query_res,
JSON_VALUE
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.name'
) as json_value_res
from dual;輸出結(jié)果:
JSON_QUERY_RES JSON_VALUE_RES
--------------------------------
null cxk
區(qū)別1:json_query無法返回單值,json_value可以返回單值
查詢對象
select JSON_QUERY
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.company'
) as json_query_res,
JSON_VALUE
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.company'
) as json_value_res
from dual;輸出結(jié)果:
JSON_QUERY_RES JSON_VALUE_RES
-------------------------------- ------------------
{"name": "unknow", "staffNum": "unknow"} null
區(qū)別2:json_query能返回對象,json_value不能返回對象值
查詢普通數(shù)組
select JSON_QUERY
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.hobbies'
) as json_query_res,
JSON_VALUE
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.hobbies'
) as json_value_res
from dual;輸出結(jié)果:
JSON_QUERY_RES JSON_VALUE_RES
-------------------------------- ------------------
["sing", "dance", "rap", "basketball"] null
區(qū)別3:json_query能返回普通數(shù)組,json_value不能返回?cái)?shù)組
結(jié)合1、2、3點(diǎn),我們給hobbies加個(gè)下標(biāo)
select JSON_QUERY
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.hobbies[0]'
) as json_query_res,
JSON_VALUE
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.hobbies[0]'
) as json_value_res
from dual;輸出結(jié)果:
JSON_QUERY_RES JSON_VALUE_RES
-------------------------------- ------------------
null sing
查詢對象數(shù)組
select JSON_QUERY
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.fans'
) as json_query_res,
JSON_VALUE
('{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"},
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}', '$.fans'
) as json_value_res
from dual;輸出結(jié)果:
JSON_QUERY_RES JSON_VALUE_RES
-------------------------------- ------------------
[{"name": "ncFans1"},{"name": "ncFans2"}] null
區(qū)別4:JSON_QUERY可以匹配對象數(shù)組的值,JSON_VALUE不可以
2. JSON_EXISTS判斷某個(gè)值是否等于目標(biāo)值
這是比較難的一個(gè)步驟,我自己摸索了很久很久才懂,我這里舉例都寫到了謂動(dòng)詞的位置,實(shí)際上JSON_EXISTS是用在where后的。
案例1:找出存在name屬性的行
SELECT case when JSON_EXISTS('{"name": "John", "age": 30, "city": "New York"}', '$.name') then 'true' else 'false' end as result FROM dual;輸出:
RESULT
---------
true
但其實(shí),我們更多都是在匹配name值等于具體的值,這就要用匹配的機(jī)制,但愿你還記得前面介紹的用法
SELECT case when JSON_EXISTS('
{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}
', '$.name ? (@ == "cxk")') then 'true' else 'false' end as result FROM dual;@表示當(dāng)前name屬性這一層,值一定要用雙引號括起來
多值匹配
SELECT case when JSON_EXISTS('
{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}
', '$.company ? (@.name == "unknow" && @.staffNum == "unknow")') then 'true' else 'false' end as result FROM dual;如果是不同層級的多值匹配,建議在where后用AND連接起來,比如:
select * from mytable where JSON_EXISTS(my_cloumn, '$.name ? (@ == "cxk")') AND JSON_EXISTS(my_cloumn, '$.company ? (@.name == "unknow" && @.staffNum == "unknow")')
JSON_EXISTS可以檢索普通數(shù)組中的值
SELECT case when JSON_EXISTS('
{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}
',
'$.hobbies[*] ? (@ == "sing")')
then 'true' else 'false' end as result FROM dual;執(zhí)行結(jié)果為true
JSON_EXISTS無法檢索對象數(shù)組
SELECT case when JSON_EXISTS('
{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": [
{"name": "ncFans1"},
{"name": "ncFans2"}
]
}
',
'$.fans")')
then 'true' else 'false' end as result FROM dual;無論是$.fans還是$.fans[*]返回結(jié)果都是false,完全無法獲取對象數(shù)組中的值。無法判斷里面的值的對比情況。
JSON_EXISTS遇到空數(shù)組時(shí),返回的是true
SELECT case when JSON_EXISTS('
{
"name": "cxk",
"sex": "female",
"hobbies": ["sing", "dance", "rap", "basketball"],
"company": {"name": "unknow", "staffNum": "unknow"}
"fans": []
}
',
'$.fans")')
then 'true' else 'false' end as result FROM dual;但如果是$.fans[0]返回的是false
3. $.xx中不能用字符串拼接、不能用任何函數(shù)會報(bào)ORA-40442錯(cuò)誤
4. 比較非對象數(shù)組的值除了JSON_EXISTS可以用JSON_VALUE與JSON_QUERY
select * from mytable where JSON_VALUE(my_cloumn, '$.name') = 'cxk' and JSON_QUERY(my_cloumn, '$.fans') = '[]' -- 判空
5. JSON_QUERY不支持通配符,查詢?nèi)烤褪?.fans,不需要$.fans[*];支持使用下標(biāo)指定
6. JSON_VALUE與JSON_QUERY可以嵌套使用,比如
SELECT * from mytable where JSON_VALUE(JSON_QUERY(my_cloumn, '$.fans'), '$.name') = 'ncFans1'
這個(gè)語句就是查找出fans中有ncFans1的記錄
7. 數(shù)組判空 使用JSON_QUERY
select * from mytable where JSON_QUERY(my_cloumn, '$.fans') = '[]'
使用JSON_EXISTS
select * from mytable where not JSON_EXISTS(my_cloumn, '$.fans[0]')
8. 使用JSON_TABLE可以解決任何復(fù)雜的問題
但是JSON_TABLE本身就很復(fù)雜,如果用來篩選數(shù)據(jù),那這個(gè)語句太難維護(hù)了。
9. 在mybatis框架中,由于$.xx的語句要放在單引號中',切不能使用任何函數(shù),不能字符拼接
所以用#{}無法注入,要用${},這在有些公司是不被允許的。目前我是這么用了,等安全部門的滲透測試結(jié)果出來,是否有sql注入風(fēng)險(xiǎn)再來更新。
注意事項(xiàng)
以上提到的操作均需要在Oracle12以上版本中使用,但并不是12以上版本都能使用。這個(gè)和數(shù)據(jù)庫設(shè)置的compatible值有關(guān)。compatible必須大于12.0才可以使用。
你可以使用擁有DBA權(quán)限的用戶執(zhí)行以下語句查看compatible值
SELECT name, value FROM v$parameter WHERE name = 'compatible'
否則,你會得到這樣的一個(gè)報(bào)錯(cuò)
ORA-00406: COMPATIBLE parameter needs to be 12.0 or greater
然后你就白忙活了,因?yàn)橐话愣疾粫榱四闾氐厣塩ompatible的。沒事,我就是白忙活了。
總結(jié)
到此這篇關(guān)于oracle中對JSON數(shù)據(jù)處理的文章就介紹到這了,更多相關(guān)oracle中JSON數(shù)據(jù)處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
調(diào)整Oracle數(shù)據(jù)庫的UNDO_RETENTION參數(shù)的方法
本文介紹了如何調(diào)整Oracle數(shù)據(jù)庫的UNDO_RETENTION參數(shù),包括查看當(dāng)前值、動(dòng)態(tài)和靜態(tài)調(diào)整方法,以及注意事項(xiàng),感興趣的朋友一起看看吧2025-03-03
SQL?錯(cuò)誤?[1722]?[42000]:?ORA-01722:?無效數(shù)字解決辦法
這篇文章主要給大家介紹了關(guān)于SQL?錯(cuò)誤?[1722]?[42000]:?ORA-01722:?無效數(shù)字的解決辦法,ORA-01722是一個(gè)錯(cuò)誤代碼,當(dāng)試圖將一個(gè)字符串轉(zhuǎn)換為數(shù)字,但字符串無法轉(zhuǎn)換為數(shù)字時(shí)會出現(xiàn)這個(gè)錯(cuò)誤,需要的朋友可以參考下2024-06-06
兩種oracle創(chuàng)建字段自增長的實(shí)現(xiàn)方式
這篇文章介紹了兩種oracle創(chuàng)建字段自增長的實(shí)現(xiàn)方式,一是序列+觸發(fā)器,二是序列+顯示調(diào)用序列,需要的朋友可以參考下2015-07-07
oracle基礎(chǔ)教程之多表關(guān)聯(lián)查詢
在實(shí)際開發(fā)中每個(gè)表的信息都不是獨(dú)立的,而是若干個(gè)表之間存在一定的聯(lián)系,如果用戶查詢某一個(gè)表的信息時(shí),可能需要查詢關(guān)聯(lián)表的信息,這就是多表關(guān)聯(lián)查詢,這篇文章主要給大家介紹了關(guān)于oracle基礎(chǔ)教程之多表關(guān)聯(lián)查詢的相關(guān)資料,需要的朋友可以參考下2023-12-12
Oracle數(shù)據(jù)庫之PL/SQL使用流程控制語句
這篇文章介紹了Oracle數(shù)據(jù)庫之PL/SQL使用流程控制語句的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05

