英文:
How to extract an entire JSON element from Oracle 19c CLOB
问题
我有一个包含JSON数据的表,数据存储在一个CLOB字段中。我们从外部来源获取这些数据,最近它们更改了一些格式,导致我们的后续处理出现问题。
数据包含一个包含用户角色的对象,当格式正确时,包含与个人角色关联的位置数组。
问题出现在一个用户具有一个角色(Test Role 1),其位置仅包含花括号{},而另一个用户也具有相同的角色(Test Role 1),其位置包含有效数据时。当我们查询第二个用户的角色时,我们会得到该用户的位置数据为空。
在示例数据中,有员工ID 1(Whitbuckle, Dalongrirlum),他具有Test Role 1和Test Role 2的角色,每个角色都有一个位置{},以及员工ID 2(Longblade, Skolout),他具有Test Role 1的角色并包含有效位置数据。其他两个用户要么具有空的EntitlementJSON属性,要么具有包含有效位置数据的Test Role 3。
当我们查询数据时,员工ID 2的记录具有空的角色,即使我们明确选择只选择他们的员工ID。
请求的解决方案:
我正在编写一个验证过程,以确保识别出格式不正确的行。为此,我想选择一个用户的EntitlementJSON属性的内容到一个变量中。然后,我将检查是否存在"location":{}。如果存在,这是一个不良记录。例如,我想要看到员工ID 1的内容如下:
"Test Role 1": { "dodaac": {}, "fundCode": {}, "glRepair": {}, "location": {}, "cognos": {}, "jv": {} }, "Test Role 2": { "dodaac": {}, "fundCode": {}, "glRepair": {}, "location": {}, "cognos": {}, "jv": {} }
这里有一个示例 db<>fiddle
CREATE TABLE TEST_JSON
( PROCESS_ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
JSON_DATA CLOB CONSTRAINT check_json CHECK (JSON_DATA IS JSON)
)
LOB (JSON_DATA) STORE AS SECUREFILE (
ENABLE STORAGE IN ROW
CHUNK 8192
RETENTION
NOCACHE
LOGGING);
-- TABLE ALTERS
ALTER TABLE TEST_JSON
ADD CONSTRAINT TEST_JSON_PK
PRIMARY KEY ( PROCESS_ID ) USING INDEX
ENABLE;
set serveroutput on
declare
c clob;
BEGIN
c:= to_clob('[
{
"displayName": "Whitbuckle, Dalongrirlum",
"employeeID": "1",
"EntitlementJSON": {
"Test Role 1": {
"dodaac": {},
"fundCode": {},
"glRepair": {},
"location": {},
"cognos": {},
"jv": {}
},
"Test Role 2": {
"dodaac": {},
"fundCode": {},
"glRepair": {},
"location": {},
"cognos": {},
"jv": {}
}
},
"manager": "Urgaehilde Rubyforged",
"company": "Bloodguard Industrie"
},
{
"displayName": "Koboldbelly, Sitgrolin",
"employeeID": "4",
"EntitlementJSON": {},
"manager": "Kogrubera Orcborn",
"company": "Bloodguard Industrie"
},
{
"displayName": "Longblade, Skolout",
"employeeID": "2",
"EntitlementJSON": {
"Test Role 1": {
"location": [
"Rockwall Villa - RV",
"Thunderbluff - TB"
]
}
},
"manager": "Therrilyn Mithrilpike",
"company": "Bloodguard Industrie"
},
{
"displayName": "Warmcoat, Alfomdum",
"employeeID": "3",
"EntitlementJSON": {
"Test Role 3": {
"location": [
"ALL"
]
}
},
"manager": "Therrilyn Mithrilpike",
"company": "Bloodguard Industrie"
}
]');
INSERT INTO TEST_JSON (JSON_DATA)
VALUES (c);
commit;
END;
以下是我们运行的查询:
select process_id,
display_name,
employeeID,
manager,
listagg(TR1) within group (order by process_id, display_name, employeeID, manager) Role_TR1,
listagg(TR2) within group (order by process_id, display_name, employeeID, manager) Role_TR2,
listagg(TR3) within group (order by process_id, display_name, employeeID, manager) Role_TR3,
listagg(TR4) within group (order by process_id, display_name, employeeID, manager) Role_TR4
from (select j.process_id,
jt.display_Name,
jt.employeeID,
jt.manager,
TR1,
TR2,
TR3,
TR4
from test_json j
cross apply JSON_TABLE(j.JSON_DATA, '$[*]'
COLUMNS (display_Name VARCHAR2(200 CHAR) PATH '$.displayName',
employeeID VARCHAR2(20 CHAR) PATH '$.employeeID',
manager VARCHAR2(200 CHAR) PATH '$.manager',
nested path '$.EntitlementJSON."Test Role 1"' columns
(TR1 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 2"' columns
(TR2 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 3"' columns
(TR3 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 4"' columns
(TR4 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]')
)) jt
where process_id = 1)
--and jt.employeeID = '2')
group by process_id, employeeID, display_name, manager;
即使取消注释"and jt.employeeID = '2'"行,我们仍然
英文:
I have a table with JSON data stored in a CLOB. We get this data from an external source and recently they changed some formatting which causes issues with our post processing.
The data contains an object containing user roles and, when correctly formatted, arrays of locations associated to an individual role.
The problem arises when one user has a Role (Test Role 1) with a Location with just curly braces {} and another user also has the same role (Test Role 1) with a location with valid data. When we query the second user's roles we get null location date for that user.
In the sample data there is employeeID 1 (Whitbuckle, Dalongrirlum) who has roles of Test Role 1 and Test Role 2, each with a Location {} and employeeID 2 (Longblade, Skolout) with a role of Test Role 1 with valid locations. The other two users have either an empty EntitlementJSON attribute or Test Role 3 with valid location data.
When we query the data, employeeID 2 record has null roles even if we explicitly select only their employeeID.
Requested Solution:
I am writing a validation procedure to makes sure that rows with the bad formatting gets identified. To do this, I would like to select into a variable the contents of the EntitlementJSON attribute for a single user. I would then check for the existance of "location":{}. If it exists this is a bad record. For example, what I would like to see for employeeID 1 is:
> "Test Role 1": {
> "dodaac": {},
> "fundCode": {},
> "glRepair": {},
> "location": {},
> "cognos": {},
> "jv": {}
> },
> "Test Role 2": {
> "dodaac": {},
> "fundCode": {},
> "glRepair": {},
> "location": {},
> "cognos": {},
> "jv": {}
> }
There is an example at this db<>fiddle
Code samples
CREATE TABLE TEST_JSON
( PROCESS_ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
JSON_DATA CLOB CONSTRAINT check_json CHECK (JSON_DATA IS JSON)
)
LOB (JSON_DATA) STORE AS SECUREFILE (
ENABLE STORAGE IN ROW
CHUNK 8192
RETENTION
NOCACHE
LOGGING);
-- TABLE ALTERS
ALTER TABLE TEST_JSON
ADD CONSTRAINT TEST_JSON_PK
PRIMARY KEY ( PROCESS_ID ) USING INDEX
ENABLE;
set serveroutput on
declare
c clob;
BEGIN
c:= to_clob('[
{
"displayName": "Whitbuckle, Dalongrirlum",
"employeeID": "1",
"EntitlementJSON": {
"Test Role 1": {
"dodaac": {},
"fundCode": {},
"glRepair": {},
"location": {},
"cognos": {},
"jv": {}
},
"Test Role 2": {
"dodaac": {},
"fundCode": {},
"glRepair": {},
"location": {},
"cognos": {},
"jv": {}
}
},
"manager": "Urgaehilde Rubyforged",
"company": "Bloodguard Industrie"
},
{
"displayName": "Koboldbelly, Sitgrolin",
"employeeID": "4",
"EntitlementJSON": {},
"manager": "Kogrubera Orcborn",
"company": "Bloodguard Industrie"
},
{
"displayName": "Longblade, Skolout",
"employeeID": "2",
"EntitlementJSON": {
"Test Role 1": {
"location": [
"Rockwall Villa - RV",
"Thunderbluff - TB"
]
}
},
"manager": "Therrilyn Mithrilpike",
"company": "Bloodguard Industrie"
},
{
"displayName": "Warmcoat, Alfomdum",
"employeeID": "3",
"EntitlementJSON": {
"Test Role 3": {
"location": [
"ALL"
]
}
},
"manager": "Therrilyn Mithrilpike",
"company": "Bloodguard Industrie"
}
]');
INSERT INTO TEST_JSON (JSON_DATA)
VALUES (c);
commit;
END;
Here is the query we run:
select process_id,
display_name,
employeeID,
manager,
listagg(TR1) within group (order by process_id, display_name, employeeID, manager) Role_TR1,
listagg(TR2) within group (order by process_id, display_name, employeeID, manager) Role_TR2,
listagg(TR3) within group (order by process_id, display_name, employeeID, manager) Role_TR3,
listagg(TR4) within group (order by process_id, display_name, employeeID, manager) Role_TR4
from (select j.process_id,
jt.display_Name,
jt.employeeID,
jt.manager,
TR1,
TR2,
TR3,
TR4
from test_json j
cross apply JSON_TABLE(j.JSON_DATA, '$[*]'
COLUMNS (display_Name VARCHAR2(200 CHAR) PATH '$.displayName',
employeeID VARCHAR2(20 CHAR) PATH '$.employeeID',
manager VARCHAR2(200 CHAR) PATH '$.manager',
nested path '$.EntitlementJSON."Test Role 1"' columns
(TR1 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 2"' columns
(TR2 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 3"' columns
(TR3 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]'),
nested path '$.EntitlementJSON."Test Role 4"' columns
(TR4 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH '$.location[*]')
)) jt
where process_id = 1)
--and jt.employeeID = '2')
group by process_id, employeeID, display_name, manager;
Even when we un-comment the "and jt.employeeID = '2'" line we still get null locations for employeeID 2
答案1
得分: 1
你不需要汇总或使用 NESTED PATH
:
SELECT process_id,
display_name,
employeeID,
manager,
tr1,
tr2,
tr3,
tr4
from test_json j
CROSS APPLY JSON_TABLE(
j.JSON_DATA, '$[*]'
COLUMNS (
display_Name VARCHAR2(200 CHAR) PATH '$.displayName',
employeeID VARCHAR2(20 CHAR) PATH '$.employeeID',
manager VARCHAR2(200 CHAR) PATH '$.manager',
tr1 JSON PATH '$.EntitlementJSON."Test Role 1".location',
tr2 JSON PATH '$.EntitlementJSON."Test Role 2".location',
tr3 JSON PATH '$.EntitlementJSON."Test Role 3".location',
tr4 JSON PATH '$.EntitlementJSON."Test Role 4".location'
)
) e
WHERE j.process_id = 1
AND e.employeeID = '2';
对于示例数据,输出如下:
PROCESS_ID | DISPLAY_NAME | EMPLOYEEID | MANAGER | TR1 | TR2 | TR3 | TR4 |
---|---|---|---|---|---|---|---|
1 | Longblade, Skolout | 2 | Therrilyn Mithrilpike | ["Rockwall Villa - RV","Thunderbluff - TB"] | null | null | null |
英文:
You don't need to aggregate or use NESTED PATH
:
SELECT process_id,
display_name,
employeeID,
manager,
tr1,
tr2,
tr3,
tr4
from test_json j
CROSS APPLY JSON_TABLE(
j.JSON_DATA, '$[*]'
COLUMNS (
display_Name VARCHAR2(200 CHAR) PATH '$.displayName',
employeeID VARCHAR2(20 CHAR) PATH '$.employeeID',
manager VARCHAR2(200 CHAR) PATH '$.manager',
tr1 JSON PATH '$.EntitlementJSON."Test Role 1".location',
tr2 JSON PATH '$.EntitlementJSON."Test Role 2".location',
tr3 JSON PATH '$.EntitlementJSON."Test Role 3".location',
tr4 JSON PATH '$.EntitlementJSON."Test Role 4".location'
)
) e
WHERE j.process_id = 1
AND e.employeeID = '2';
Which, for the sample data, outputs:
PROCESS_ID | DISPLAY_NAME | EMPLOYEEID | MANAGER | TR1 | TR2 | TR3 | TR4 |
---|---|---|---|---|---|---|---|
1 | Longblade, Skolout | 2 | Therrilyn Mithrilpike | ["Rockwall Villa - RV","Thunderbluff - TB"] | null | null | null |
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论