如何从Oracle 19c的CLOB中提取整个JSON元素

huangapple go评论58阅读模式
英文:

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(&#39;[
{
&quot;displayName&quot;: &quot;Whitbuckle, Dalongrirlum&quot;,
&quot;employeeID&quot;: &quot;1&quot;,
&quot;EntitlementJSON&quot;: {
&quot;Test Role 1&quot;: {
&quot;dodaac&quot;: {},
&quot;fundCode&quot;: {},
&quot;glRepair&quot;: {},
&quot;location&quot;: {},
&quot;cognos&quot;: {},
&quot;jv&quot;: {}
},
&quot;Test Role 2&quot;: {
&quot;dodaac&quot;: {},
&quot;fundCode&quot;: {},
&quot;glRepair&quot;: {},
&quot;location&quot;: {},
&quot;cognos&quot;: {},
&quot;jv&quot;: {}
}
},
&quot;manager&quot;: &quot;Urgaehilde Rubyforged&quot;,
&quot;company&quot;: &quot;Bloodguard Industrie&quot;
},
{
&quot;displayName&quot;: &quot;Koboldbelly, Sitgrolin&quot;,
&quot;employeeID&quot;: &quot;4&quot;,
&quot;EntitlementJSON&quot;: {},
&quot;manager&quot;: &quot;Kogrubera Orcborn&quot;,
&quot;company&quot;: &quot;Bloodguard Industrie&quot;
},
{
&quot;displayName&quot;: &quot;Longblade, Skolout&quot;,
&quot;employeeID&quot;: &quot;2&quot;,
&quot;EntitlementJSON&quot;: {
&quot;Test Role 1&quot;: {
&quot;location&quot;: [
&quot;Rockwall Villa - RV&quot;,
&quot;Thunderbluff - TB&quot;
]
}
},
&quot;manager&quot;: &quot;Therrilyn Mithrilpike&quot;,
&quot;company&quot;: &quot;Bloodguard Industrie&quot;
},
{
&quot;displayName&quot;: &quot;Warmcoat, Alfomdum&quot;,
&quot;employeeID&quot;: &quot;3&quot;,
&quot;EntitlementJSON&quot;: {
&quot;Test Role 3&quot;: {
&quot;location&quot;: [
&quot;ALL&quot;
]
}
},
&quot;manager&quot;: &quot;Therrilyn Mithrilpike&quot;,
&quot;company&quot;: &quot;Bloodguard Industrie&quot;
}
]&#39;);
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, &#39;$[*]&#39;
COLUMNS (display_Name                  VARCHAR2(200 CHAR)  PATH &#39;$.displayName&#39;,
employeeID                    VARCHAR2(20 CHAR) PATH &#39;$.employeeID&#39;,
manager                       VARCHAR2(200 CHAR) PATH &#39;$.manager&#39;,       
nested path &#39;$.EntitlementJSON.&quot;Test Role 1&quot;&#39; columns
(TR1 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH &#39;$.location[*]&#39;),      
nested path &#39;$.EntitlementJSON.&quot;Test Role 2&quot;&#39; columns
(TR2 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH &#39;$.location[*]&#39;),      
nested path &#39;$.EntitlementJSON.&quot;Test Role 3&quot;&#39; columns
(TR3 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH &#39;$.location[*]&#39;),      
nested path &#39;$.EntitlementJSON.&quot;Test Role 4&quot;&#39; columns
(TR4 VARCHAR2(4000 CHAR) FORMAT JSON WITH WRAPPER PATH &#39;$.location[*]&#39;)      
)) jt
where process_id = 1)
--and jt.employeeID = &#39;2&#39;)
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

fiddle

英文:

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, &#39;$[*]&#39;
COLUMNS (
display_Name VARCHAR2(200 CHAR) PATH &#39;$.displayName&#39;,
employeeID   VARCHAR2(20 CHAR)  PATH &#39;$.employeeID&#39;,
manager      VARCHAR2(200 CHAR) PATH &#39;$.manager&#39;,
tr1         JSON               PATH &#39;$.EntitlementJSON.&quot;Test Role 1&quot;.location&#39;,
tr2         JSON               PATH &#39;$.EntitlementJSON.&quot;Test Role 2&quot;.location&#39;,
tr3         JSON               PATH &#39;$.EntitlementJSON.&quot;Test Role 3&quot;.location&#39;,
tr4         JSON               PATH &#39;$.EntitlementJSON.&quot;Test Role 4&quot;.location&#39;
)
) e
WHERE  j.process_id = 1
AND    e.employeeID = &#39;2&#39;;

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

fiddle

huangapple
  • 本文由 发表于 2023年2月19日 02:47:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495613.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定