Skip to Main Content
  • Questions
  • JSON support in 12c - fetching array values

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Lal.

Asked: February 15, 2016 - 2:10 pm UTC

Last updated: February 23, 2016 - 1:44 am UTC

Version: 12.1.0.2

Viewed 10K+ times! This question is

You Asked

Hi,

I have a query on the JSON feature of 12c. I have the following JSON data

CREATE TABLE jsontry( id VARCHAR2(11), 
   str CLOB,
   CONSTRAINT jsontry_PK PRIMARY KEY (id), 
   CONSTRAINT jsontry_CHK CHECK (str IS JSON) ENABLE
 );

Insert into jsontry values ('1','{"a":"100","b":"200"}');
Insert into jsontry values ('2','{"c":"300"}');
Insert into jsontry values ('3','{"a":"400","c":"500","d":["100","200","300","400"]}');
commit;

SELECT a.id,jt.*
FROM   jsontry a,
       JSON_TABLE(str, '$.*'
         COLUMNS ( 
           "Attribute" varchar2(5) PATH '$',
           "Value" varchar2(5) PATH '$'
       )) "JT";
       
ID          ATTRIBUTE VALUE
----------- --------- -----
1           100       100   
1           200       200   
2           300       300   
3           400       400   
3           500       500   
3                           

 6 rows selected


The issue is that i want the column values to be shown for the attribute column and also i need the array value for id 3 to be also converted to attribute and value as shown below

Expected Output

ID          ATTRIBUTE VALUE
----------- --------- -----
1           a         100   
1           b         200   
2           c         300   
3           a         400   
3           c         500   
3           d         100  
3           d         200  
3           d         300  
3           d         400 


I tried a lot using the available JSON functions, but couldnt find any options to get the columns.
As always expecting a solution for my query,


and Chris said...

There's a few parts to this. First you need to get the attribute names. I'm not aware of a way to do this dynamically.

So you need to inspect your document and list out the attributes you want in the columns clause:

select a.id, jt.*
from   jsontry a,
       json_table(str, '$'
         columns ( 
           "a" varchar2(5) path '$.a',
           "b" varchar2(5) path '$.b',
           "c" varchar2(5) path '$.c',
           "d" varchar2(5) path '$.d'
         )
       ) jt;


Next you need to extract the elements of the array d. You can do this using the nested path clause:

select a.id,jt.*
from   jsontry a,
       json_table(str, '$'
         columns ( 
           "a" varchar2(5) path '$.a',
           "b" varchar2(5) path '$.b',
           "c" varchar2(5) path '$.c',
           nested path '$.d[*]' columns (
             "d" path '$' null on error
           )
         ) 
       ) "JT";

ID          A     B     C     D                  
----------- ----- ----- ----- --------------------
1           100   200                             
2                       300                       
3           400         500   100                 
3           400         500   200                 
3           400         500   300                 
3           400         500   400


Finally to convert these into rows you'll need to unpivot:

select * from (
  select a.id,jt.*
  from   jsontry a,
         json_table(a.str, '$'
           columns ( 
             "a" varchar2(5) path '$.a',
             "b" varchar2(5) path '$.b',
             "c" varchar2(5) path '$.c',
             nested path '$.d[*]' columns (
               "d" path '$' null on error
             )
           ) 
         ) "JT"
)
unpivot (
  val for attrib in (
    a as 'a', b as 'b', c as 'c', d as 'd'
  )
)
order by 1, 2, 3;

ID          A VAL      
----------- - ----------
1           a 100       
1           b 200       
2           c 300       
3           a 400       
3           a 400       
3           a 400       
3           a 400       
3           c 500       
3           c 500       
3           c 500       
3           c 500       
3           d 100       
3           d 200       
3           d 300       
3           d 400       

 15 rows selected


Note the values for 3 a and c are repeated. To avoid this you'll need to manipulate the results so the non-array items only appear once. One way to do this is with a case expression like the following:

case 
  when row_number() over (partition by a.id order by d) = 1 then
    jt.a
end


If you want to read more about the nested table option, head to:

https://blogs.oracle.com/jsondb/entry/the_new_sql_json_query3

Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

JSON support for 12c

Lal Cyril, February 16, 2016 - 1:00 pm UTC

Hi,

Thanks very much for the quick response.
Is it possible to retrieve the column names (in the example mentioned a,b,c and d are the column names).
Chris Saxon
February 16, 2016 - 2:54 pm UTC

I'm not aware of a way to do this.

Reading Json data from Internet/Intranet

Rajesh Pahade, February 19, 2016 - 8:31 am UTC

Hi Tom,

I would like to store the JSON data, which is available over Internet to Oracle 12c table.

What is the best way of downloading the JSON data, which is accessible via browser from Oracle database server and store it to oracle table.
Do I have to download JSON document separately from internet (http/https) and then process that file to load the data in oracle table or do I have some simple direct option available to directly load data to the table.

Thanks for your help.

Regards,
Rajesh Pahade

Connor McDonald
February 20, 2016 - 4:12 am UTC

You can use UTL_HTTP to fetch data over http. See https://asktom.oracle.com/pls/asktom/f?p=100:11:::NO:RP:P11_QUESTION_ID:285215954607 for an example.

And then piecewise append into a clob into the table of your choice.

Also, you might want to check out ORDS ( http://www.oracle.com/technetwork/developer-tools/rest-data-services/overview/index.html ) if the json is actually obtained via REST.

Hope this helps,
Connor

failing to load the large Json output

Rajesh Pahade, February 22, 2016 - 12:54 pm UTC

Hi Tom,

Thank you tom for your valuable responses.

I tried following and it worked for small Json output. But when tried for large json output its failing. If I try with first 1000 lines it works fine, but when try to load full Joson output which is large in size, it fails. I am using Oracle 12c database.

Create table testtable
(
JSONdata club
);

declare
l_page utl_http.html_pieces;
vclob clob;

begin
l_page := utl_http.request_pieces( url => ' https://raw.githubusercontent.com/zemirco/sf-city-lots-json/master/citylots.json',
proxy => 'webproxy.lon.corp.services',
wallet_path => 'file:\wallet',
wallet_password => 'WalletPassword'
);

dbms_output.put_line( l_page.count );
for i in 1 .. l_page.count
loop
vclob := vclob ||l_page(i);
-- exit when ( i = 1000 );
end loop;
insert into testtable values (vclob);
end;
/


Error report -
ORA-64204: encountered partial multibyte character
64204. 00000 - "encountered partial multibyte character"
*Cause: The requested operation could not complete because a partial
multibyte character was found at the end of the input.
*Action: Ensure that the input buffer ends with a complete multibyte character
and retry the operation.


Regards,
Rajesh Pahade
Connor McDonald
February 23, 2016 - 1:44 am UTC

Perhaps try it without using "html_pieces", eg

SQL> create table t ( c clob );

Table created.

SQL>
SQL> set serverout on
SQL> declare
  2    l_url            varchar2(100) := 'https://raw.githubusercontent.com/zemirco/sf-city-lots-json/master/citylots.json';
  3    l_req            utl_http.req;
  4    l_result         utl_http.resp;
  5    l_data           varchar2(32767);
  6    l_clob           clob;
  7  begin
  8    dbms_lob.createtemporary(l_clob,true);
  9
 10    utl_http.set_wallet('file:C:\oracle\wallet', '*******');
 11    l_req  := utl_http.begin_request(l_url);
 12    l_result := utl_http.get_response(l_req);
 13
 14    begin
 15      loop
 16        utl_http.read_text(l_result, l_data, 16000);
 17        dbms_lob.writeappend(l_clob,length(l_data),l_data);
 18        --dbms_output.put_line (l_data);
 19      end loop;
 20    exception
 21      when utl_http.end_of_body then
 22        utl_http.end_response(l_result);
 23    end;
 24
 25    insert into t values (l_clob);
 26    commit;
 27    dbms_lob.freetemporary(l_clob);
 28  end;
 29  /

PL/SQL procedure successfully completed.

SQL>
SQL> select dbms_lob.getlength(c) from t;

DBMS_LOB.GETLENGTH(C)
---------------------
            189778220


Cheers,
Connor

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.