35
file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM] Disclaimer The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle. SQL/JSON Features in Database 12.2 Oracle Database 12c Release 2 (12.2), the latest generation of the world’s most popular database is now available in the Oracle Cloud. The first release of Oracle Database 12c included significant new SQL functionality for working with JSON documents stored in the database. Oracle Database 12c Release 2 includes five major areas of new JSON related functionality 1. Integration with Oracle Database In-Memory. Customers who are licensed for the Oracle Database In-Memory option will be able to use its capabilities to accelerate SQL queries over JSON content. 2. JSON Data Guide. The data guide allows developers to generate and query metadata that describes the JSON documents they have stored in Oracle Database. The JSON Data Guide can also be used to generate JSON schema documents and relational views that reflect the structure of the JSON content that has been stored in the database. 3. PL/SQL Integration. Oracle Database 12c includes a new PL/SQL document API that makes it easy to use JSON path expressions to access and update the contents of a JSON document. 4. JSON Generation. Oracle Database 12c includes new SQL operators that allow the result of a SQL query to be transformed into one or more JSON documents. 5. Spatial Integration. Oracle Database 12c includes support for using Oracle Spatial to index and query GeoJSON content. GeoJSON is a popular way of embedding spatial information, such as locations, into JSON documents. The tutorial focuses on items 2 thru 5. Modules 1. Setup 2. Storing JSON Documents in Oracle Database 12c 3. Accessing JSON content using Oracle's Simplied Syntax For JSON 4. Accessing JSON using JSON_VALUE and JSON_QUERY 5. Relational access to JSON content using JSON_TABLE 6. Filtering result sets using JSON_EXISTS 7. Indexing JSON documents stored in Oracle Database 12c 8. JSON Search Index

SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

Embed Size (px)

Citation preview

Page 1: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

Disclaimer

The following is intended to outline our general product direction. It is intended for information purposes only, and maynot be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and shouldnot be relied upon in making purchasing decisions. The development, release, and timing of any features orfunctionality described for Oracle’s products remains at the sole discretion of Oracle.

SQL/JSON Features in Database 12.2Oracle Database 12c Release 2 (12.2), the latest generation of the world’s most popular database is now available in theOracle Cloud. The first release of Oracle Database 12c included significant new SQL functionality for working withJSON documents stored in the database. Oracle Database 12c Release 2 includes five major areas of new JSON relatedfunctionality

1. Integration with Oracle Database In-Memory.

Customers who are licensed for the Oracle Database In-Memory option will be able to use its capabilities toaccelerate SQL queries over JSON content.

2. JSON Data Guide.

The data guide allows developers to generate and query metadata that describes the JSON documents they havestored in Oracle Database. The JSON Data Guide can also be used to generate JSON schema documents andrelational views that reflect the structure of the JSON content that has been stored in the database.

3. PL/SQL Integration.

Oracle Database 12c includes a new PL/SQL document API that makes it easy to use JSON path expressions toaccess and update the contents of a JSON document.

4. JSON Generation.

Oracle Database 12c includes new SQL operators that allow the result of a SQL query to be transformed into oneor more JSON documents.

5. Spatial Integration.

Oracle Database 12c includes support for using Oracle Spatial to index and query GeoJSON content. GeoJSON isa popular way of embedding spatial information, such as locations, into JSON documents.

The tutorial focuses on items 2 thru 5.

Modules

1. Setup2. Storing JSON Documents in Oracle Database 12c3. Accessing JSON content using Oracle's Simplied Syntax For JSON4. Accessing JSON using JSON_VALUE and JSON_QUERY5. Relational access to JSON content using JSON_TABLE6. Filtering result sets using JSON_EXISTS7. Indexing JSON documents stored in Oracle Database 12c8. JSON Search Index

Page 2: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

9. JSON Data Guide10. JSON Generation11. PL/SQL Integration: Introducing the PL/SQL API for JSON12. Spatial Integration and Support for GeoJSON

1. Setup

1.1 Ensure the HR Sample Schema is available.

The Hands-on-Lab uses data from the HR Schema. The HR Schema is installed as part of the Oracle Database SampleSchemas. If the Oracle Database Sample Schemas have not been installed please follow the instructions found on theOracle Sample Schemas GitHub Repository to install them.

1.2 Create the required user.

Connect to the database as an administrator and execute the following statements to create the user that will be usedwhen running the Hands-on-Lab. This will ensure that the user has been granted the necessary permissions. Note thatthe following code will create a user called STUDENT1, if necessary replace STUDENT1 with an appropriate valuebefore executing the code in your environment.

---- Grant access tables in HR Schema.--def USERNAME=STUDENT1--grant CREATE SESSION, RESOURCE to &USERNAME identified by &USERNAME/grant UNLIMITED TABLESPACE to HOLTEST1/grant CREATE VIEW to HOLTEST1/grant all on HR.COUNTRIES to &USERNAME/grant all on HR.LOCATIONS to &USERNAME/grant all on HR.DEPARTMENTS to &USERNAME/grant all on HR.EMPLOYEES to &USERNAME/grant all on HR.JOBS to &USERNAME/grant all on HR.JOB_HISTORY to &USERNAME/ALTER USER &USERNAME PASSWORD EXPIRE/

Once the user has been created connect to the database as the newly created user set a new password.

1.3 Obtain sample data.

The test data required to run this Hands-on-Lab can be downloaded from the following locations. Save the files to afolder on your local file system by right-clicking on the following links and performing a "Save As" operation on eachfile.

PurchaseOrders.dmp: db-sample-schemas repository on GitHub

Page 3: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

CityLots.json: CityLot Data For San Francisco County on GitHub

1.4 Stage the sample data files

If you have write permissions on a folder that can be accessed using the Oracle Database's BFILE mechanism copythese files to that folder and create a SQL DIRECTORY object that points to that folder. (Replace "/tmp" in thefollowing example with the path to the folder in question'. Note you will need to have been granted the "CREATE ANYDIRECTORY" privilege to execute this SQL.

create or replace DIRECTORY JOSN_HOL_FILES as '/tmp'/

1.5 Stage the sample data files to the Oracle XML DB repository using SQL Loader.

Run the follow statements to create the objects required to allow SQL*LOADER to load files into the XDB Repository.Note you will need to have been granted the "CREATE VIEW" privilege to execute this SQL.

-- create table SQLLDR_REPOSITORY_TABLE ( RESULT NUMBER ) / create or replace view SQLLDR_REPOS_INTERFACE_VIEW as SELECT RESULT "RESULT", CAST(NULL AS VARCHAR2(1024)) "RESOURCE_PATH", CAST(NULL AS VARCHAR2(5)) "OVERWRITE", TO_BLOB(NULL) "CONTENT" from SQLLDR_REPOSITORY_TABLE / create or replace trigger SQLLDR_REPOS_INTERFACE_TRIGGER instead of INSERT on SQLLDR_REPOS_INTERFACE_VIEW begin null; end; / create or replace package SQLLDR_REPOSITORY_INTERFACE authid CURRENT_USER as function UPLOAD_CONTENT(P_RESOURCE_PATH VARCHAR2, P_OVERWRITE VARCHAR2, P_CONTENT BLOB) return NUMBER; end; / show errors -- create or replace package body SQLLDR_REPOSITORY_INTERFACE as -- function UPLOAD_CONTENT(P_RESOURCE_PATH VARCHAR2, P_OVERWRITE VARCHAR2, P_CONTENT BLOB) return NUMBER as V_RESULT BOOLEAN; V_PARENT_PATH VARCHAR2(700) := '/'; begin -- Delete any existing resource if (DBMS_XDB.existsResource(P_RESOURCE_PATH)) then if (P_OVERWRITE = 'TRUE') then

Page 4: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

DBMS_XDB.deleteResource(P_RESOURCE_PATH); else return 0; end if; end if; -- Ensure the Target Folder Exists while (instr(substr(P_RESOURCE_PATH,length(V_PARENT_PATH)+1),'/') > 0) loop V_PARENT_PATH := substr(P_RESOURCE_PATH,1,instr(substr(P_RESOURCE_PATH,length(V_PARENT_PATH)+1),'/') + length(V_PARENT_PATH)-1); if not(DBMS_XDB.existsResource(V_PARENT_PATH)) then V_RESULT := DBMS_XDB.createFolder(V_PARENT_PATH); end if; V_PARENT_PATH := V_PARENT_PATH || '/'; end loop; -- Create the new Resource; V_RESULT := DBMS_XDB.createResource(P_RESOURCE_PATH,P_CONTENT); return 1; end; -- end; / show errors -- create or replace function READ_LINES(P_RESOURCE_PATH VARCHAR2)return XDB.XDB$STRING_LIST_T pipelinedas V_CONTENT CLOB := XDBURITYPE(P_RESOURCE_PATH).getClob(); V_CONTENT_LENGTH PLS_INTEGER := DBMS_LOB.getLength(V_CONTENT); V_AMOUNT PLS_INTEGER := 32767; V_OFFSET PLS_INTEGER := 1;

V_BUFFER VARCHAR2(32767); V_BUFFER_LENGTH PLS_INTEGER; V_BUFFER_OFFSET PLS_INTEGER := 1; V_NEWLINE_INDEX PLS_INTEGER := 0; V_NEXT_LINE VARCHAR2(32767);begin while (V_OFFSET < V_CONTENT_LENGTH ) loop DBMS_LOB.READ(V_CONTENT,V_AMOUNT,V_OFFSET,V_BUFFER); V_OFFSET := V_OFFSET + V_AMOUNT; V_BUFFER_LENGTH := V_AMOUNT; V_AMOUNT := 32767; V_BUFFER_OFFSET := 1; V_NEWLINE_INDEX := INSTR(V_BUFFER,CHR(10),V_BUFFER_OFFSET); WHILE (V_NEWLINE_INDEX > 0) loop V_NEXT_LINE := V_NEXT_LINE || SUBSTR(V_BUFFER,V_BUFFER_OFFSET,(V_NEWLINE_INDEX-V_BUFFER_OFFSET)); pipe row (V_NEXT_LINE); V_NEXT_LINE := NULL; V_BUFFER_OFFSET := V_NEWLINE_INDEX + 1; if (SUBSTR(V_BUFFER,V_BUFFER_OFFSET,1) = CHR(13)) then V_BUFFER_OFFSET := V_BUFFER_OFFSET + 1; end if; V_NEWLINE_INDEX := INSTR(V_BUFFER,CHR(10),V_BUFFER_OFFSET); end loop; V_NEXT_LINE := SUBSTR(V_BUFFER,V_BUFFER_OFFSET); end loop; if (length(V_NEXT_LINE) > 0) then pipe row (V_NEXT_LINE); end if;end; /show errors--

Page 5: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

Create a SQL Loader control file called SAMPLE_DATA_FILES.ctl. The content of the control file is shown below.Place this file in the folder that conains the sample data files.

load data infile *append into table SQLLDR_REPOS_INTERFACE_VIEW ( SOURCE FILLER CHAR(32), OVERWRITE CHAR(5), FILLER FILLER CHAR(1), RESOURCE_PATH CHAR(64), CONTENT lobfile(SOURCE) TERMINATED BY EOF, RESULT EXPRESSION "SQLLDR_REPOSITORY_INTERFACE.UPLOAD_CONTENT(:RESOURCE_PATH,:OVERWRITE,:CONTENT)" ) begindatapurchaseOrders.dmp TRUE /public/tutorials/json/testdata/purchaseOrders.dmpcityLots.json TRUE /public/tutorials/json/testdata/cityLots.json

Execute the following command to load the sample data files into the XMLDB Repository

sqlldr STUDENT1@TNSALIAS control=SAMPLE__DATAFILES.ctl

1.6 Initialize the Tutorial.

Run the following statements to initialize the Tutorial. These statements can also be used to reset the tutorial if you wantto start again at any point.

create or replace view EMPLOYEE_KEY_VALUEasselect EMPLOYEE_ID as ID, 'EmployeeId' as KEY, to_char(EMPLOYEE_ID) as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'FirstName' as KEY, FIRST_NAME as VALUE from HR.EMPLOYEES union all select EMPLOYEE_ID as ID, 'LastName' as KEY, LAST_NAME as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'EmailAddress' as KEY, EMAIL as VALUE from HR.EMPLOYEES union all select EMPLOYEE_ID as ID, 'TelephoneNumber' as KEY, PHONE_NUMBER as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID,

Page 6: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

'HireDate' as KEY, to_char(HIRE_DATE) as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'JobId' as KEY, JOB_ID as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'Salary' as KEY, to_char(SALARY) as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'Commision' as KEY, to_char(COMMISSION_PCT) as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'ManagerId' as KEY, to_char(MANAGER_ID) as VALUE from HR.EMPLOYEESunion all select EMPLOYEE_ID as ID, 'DepartmentId' as KEY, to_char(DEPARTMENT_ID) as VALUE from HR.EMPLOYEES/ declare cursor getTable is select TABLE_NAME from ALL_TABLES where TABLE_NAME in ('J_PURCHASEORDER', 'JSON_DUMP_CONTENTS','CITY_LOT_FEATURES') and OWNER = SYS_CONTEXT('USERENV','CURRENT_USER'); begin for t in getTable() loop execute immediate 'DROP TABLE "' || t.TABLE_NAME || '" PURGE'; end loop;end;/

2. Storing JSON Documents in Oracle Database 12c

Create a simple table to store JSON documents

In Oracle there is no dedicated JSON data type. JSON documents are stored in the database using standard Oracle datatypes such as VARCHAR2, CLOB and BLOB. VARCHAR2 can be used where the size of the JSON document willnever exceed 4K (32K in database where when LONG VARCHAR support has been enabled.) Larger documents arestored using CLOB or BLOB data types.

In order to ensure that the content of the column is valid JSON data, a new constraint IS JSON, is provided that can beapplied to a column. This constraint returns TRUE if the content of the column is well formatted JSON and FALSEotherwise

This first statement in this module creates a table which will be used to contain JSON documents.

create table J_PURCHASEORDER ( ID RAW(16) NOT NULL, DATE_LOADED TIMESTAMP(6) WITH TIME ZONE, PO_DOCUMENT CLOB CHECK (PO_DOCUMENT IS JSON)

Page 7: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

)/

This statement creates a very simple table, J_PURCHASEORDER. The table has a column PO_DOCUMENT of typeCLOB. The IS JSON constraint is applied to the column PO_DOCUMENT, ensuring that the column can store onlywell formed JSON documents.

Loading JSON Documents into the database

JSON documents can come from a number of different sources. Since Oracle stores JSON data using standard SQL datatypes, all of the popular Oracle APIs can be used to load JSON documents into the database. JSON documentscontained in files can be loaded directly into the database using External Tables.

This statement creates a simple external table that can read JSON documents from a dump file generated by a typicalNo-SQL style database. In this case, the documents are contained in the file purchaseOrders.json. The SQL directoryobject JOSN_HOL_FILES points to the folder containing the dump file, and this folder also contain any log or bad filesgenerated when the External Table is processed.

Note External Tables are not supported when using Oracle Cloud managed database services such as ExadataExpress.

CREATE TABLE DUMP_FILE_CONTENTS( PO_DOCUMENT CLOB)ORGANIZATION EXTERNAL( TYPE ORACLE_LOADER DEFAULT DIRECTORY ORDER_ENTRY ACCESS PARAMETERS ( RECORDS DELIMITED BY 0x'0A' BADFILE JOSN_HOL_FILES: 'JSON_DUMPFILE_CONTENTS.bad' LOGFILE JOSN_HOL_FILES: 'JSON_DUMPFILE_CONTENTS.log' FIELDS( JSON_DOCUMENT CHAR(5000) ) ) LOCATION ( JOSN_HOL_FILES:'purchaseOrders.dmp' ))PARALLELREJECT LIMIT UNLIMITED/

The following code creates a view which will act as the equivilant of the external table in the above example. Thisalternative should be used when running the Hands-on-Lab in an environment where SQL DIRECTORY objects are notavailable, such as when using the Oracle Exadata Express cloud service. This view loads the dump file from the OracleXMLDB repository.

create or replace view JSON_DUMP_CONTENTS ( PO_DOCUMENT ) as select COLUMN_VALUE JSON_DOCUMENT from TABLE(READ_LINES('/public/tutorials/json/testdata/purchaseOrders.dmp'))/

Page 8: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The following statement copies the JSON documents from the dump file into the J_PURCHASEORDER table.

insert into J_PURCHASEORDER select SYS_GUID(), SYSTIMESTAMP, PO_DOCUMENT from JSON_DUMP_CONTENTS where PO_DOCUMENT IS JSON and rownum < 1001 / commit /

The IS JSON condition is used to ensure that the insert operation takes place only for well formed documents. Makesure that the commit statement is executed after the insert statement has completed. SYS_GUID and SYSTIMESTAMPare used to populate columns ID and DATE_LOADED.

3. Accessing JSON content using Oracle's Simplied Syntax For JSON

Oracle 12c allows a simple "dotted" notation to be used to perform a limited set of operations on columns containingJSON. It also introduces a set of SQL operators that allow the full power of the JSON path language to be used withJSON. This is similar to the way in which the database allows XQuery to be used to access the contents of XMLdocuments stored in the database.

Accessing JSON using simplified syntax

The dotted notation can be used to perform basic operations on JSON stored in Oracle 12c. Using the dotted notationyou can access the value of any keys contained in the JSON document. In order to use the dotted notation, a table aliasmust be assigned to the table in the FROM clause, and any reference to the JSON column must be prefixed with theassigned alias. All data is returned as VARCHAR2(4000).

The following examples demonstrate how to use the simplified syntax to extract values from an JSON document andhow to filter a result set based on the content of a JSON document. The first query shows the count of PurchaseOrderdocuments by cost center

select j.PO_DOCUMENT.CostCenter, count(*) from J_PURCHASEORDER j group by j.PO_DOCUMENT.CostCenter order by j.PO_DOCUMENT.CostCenter/

The second query shows how to fetch the JSON document where the PONumber key has the value 450:

select j.PO_DOCUMENT from J_PURCHASEORDER j where j.PO_DOCUMENT.PONumber = 450/

The third query shows how to fetch data from any document where the key PONumber contains the value 450. The

Page 9: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

statement returns the values of the keys Reference, Requestor and CostCenter as well as the value of the key city whichis a property of the object identified by the key Address which in turn is a property of the object identified by the keyShippingInstructions

select j.PO_DOCUMENT.Reference, j.PO_DOCUMENT.Requestor, j.PO_DOCUMENT.CostCenter, j.PO_DOCUMENT.ShippingInstructions.Address.city from J_PURCHASEORDER j where j.PO_DOCUMENT.PONumber = 450/

The fourth query shows how to fetch the content of the Address object:

select j.PO_DOCUMENT.ShippingInstructions.Address from J_PURCHASEORDER j where j.PO_DOCUMENT."PONumber" = 450/

The Address object is returned as JSON text in a VARCHAR2(4000).

4. Accessing JSON using JSON_VALUE and JSON_QUERY

Accessing scalar values using JSON_VALUE

The JSON_VALUE operator uses a JSON path expression to access a single scalar value. It is typically used in theselect list to return the value associated with the JSON path expression or in the WHERE clause to filter a result setbased on the content of a JSON document.

JSON_VALUE takes two arguments, a JSON column and a JSON path expression. It provides a set of modifiers thatallow control over the format in which the data is returned and how to handle any errors encountered while evaluatingthe JSON path expression.

The following examples demonstrate how to use the JSON_VALUE operator to extract scalar values from a JSONdocument using JSON path expressions and to filter a result set based on the content of a JSON document. The firstquery shows the count of PurchaseOrder documents by cost center:

select JSON_VALUE(PO_DOCUMENT,'$.CostCenter'), count(*) from J_PURCHASEORDER group by JSON_VALUE(PO_DOCUMENT ,'$.CostCenter') /

The query uses the JSON_VALUE operator to return the value of the CostCenter key from each document in theJ_PURCHASEORDER table. The SQL Count and Group operators are then applied to the values returned, to generatethe desired result. This shows how by storing JSON documents in the Oracle Database makes it possible to bring the fullpower of SQL to bear on JSON content without giving up any of the advantages of the JSON paradigm.

Page 10: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The second query shows how to access a value from within an array. This example uses index 0 to access the value ofthe UPCCode from the Part object inside the first member of the LineItems array. This query also shows how to useJSON_VALUE as a filter in the WHERE clause of the SQL statement, so that the JSON_VALUE operator in the selectlist is executed only for those rows where the JSON_VALUE based condition in the WHERE clause evaluates toTRUE.

select JSON_VALUE(PO_DOCUMENT,'$.LineItems[0].Part.UPCCode') from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

JSON_VALUE also provides control over the value returned. You can specify size when returning a VARCHAR2value, and size and precision when returning a NUMBER value. The third query shows how to specify the SQL datatype that is returned by JSON_VALUE. In this case, the value is returned as a NUMBER(5,3) value.

select JSON_VALUE(PO_DOCUMENT,'$.LineItems[0].Part.UnitPrice' returning NUMBER(5,3)) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

The JSON_VALUE function also provides options for handling errors that might be encountered when applying theJSON PATH expression to a JSON document. Options available include:

NULL ON ERROR: The default. If an error is encountered while applying a JSON path expression to a JSONdocument the result is assumed to be SQL NULL and no error is raised.ERROR ON ERROR: An error is raised in the event that an error is encountered while applying a JSON pathexpression to a JSON document.DEFAULT on ERROR: The developer specifies a literal value that is returned in the event that an error isencountered while applying a JSON path expression to a JSON document.

The most common source of an error with JSON_VALUE is that the JSON path expression resolves to something otherthan a scalar value. JSON, by its nature is highly flexible, which raises the possibility of having a small number ofdocuments that do not conform to a particular pattern. The NULL ON ERROR behavior ensures that a single errorcaused by one outlier does not abort an entire operation.

The fourth query shows the default behavior for JSON_VALUE in the event of an error during JSON path evaluation.

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstruction.Address') from J_PURCHASEORDER where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450/

In this example, there is an error in the first key in the JSON path expression. The first key should beShippingInstructions, not ShippingInstruction. Since the JSON path expression does match a scalar value, the NULLON ERROR behavior kicks in and the JSON_VALUE operator returns NULL.

The fifth query demonstrates the DEFAULT on ERROR behavior

Page 11: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstruction.Address' DEFAULT 'N/A' ON ERROR) from J_PURCHASEORDER where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the JSON path expression does not evaluate to a scalar value, when the JSON path expression is evaluated theDEFAULT on ERROR behavior kicks in, and the JSON_VALUE operator returns "N/A".

The sixth query demonstrates the ERROR ON ERROR behavior.

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstruction.Address' ERROR ON ERROR) from J_PURCHASEORDER where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450/

Since the JSON path expression does not evaluate to a scalar value, when the JSON path expression is evaluated theERROR ON ERROR behavior kicks in and results in error "ORA-40462: JSON_VALUE evaluated to no value" beingraised.

The seventh query is another example of the ERROR ON ERROR behavior.

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstructions.Address' ERROR ON ERROR) from J_PURCHASEORDER where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

In this case, the JSON path expression does evaluate to a key in the document; however the value associated with thekey is an object, not a scalar. Since JSON_VALUE can only return a scalar value, when the JSON path expression isevaluated the ERROR ON ERROR behavior kicks in and results in error "ORA-40456: JSON_VALUE evaluated tonon-scalar value" being raised.

The eighth query shows that the error handling clauses only apply to runtime errors that arise when the JSON pathexpression is applied to a set of JSON documents:

select JSON_VALUE(PO_DOCUMENT, '$.ShippingInstructions,Address' NULL ON ERROR) from J_PURCHASEORDER where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

In this example, the error is that a comma, rather than a period, has been used as the separator between the first andsecond keys in the JSON path expression. Despite the fact that NULL ON ERROR semantics were requested, when thestatement is executed error "ORA-40597: JSON path expression syntax error ('$.ShippingInstructions,Address') JZN-00209: Unexpected characters after end of path at position 23" is raised due to the fact that an invalid JSON pathexpression is a compile time error rather than a runtime error.

Accessing objects and arrays using JSON_QUERY

JSON_QUERY is the complement of JSON_VALUE. Whereas JSON_VALUE can return only a scalar value,

Page 12: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

JSON_QUERY can return only an object or an array. Like JSON_VALUE, JSON_QUERY takes two arguments, aJSON column and a JSON path expression. It provides a set of modifiers that allow control over the format in which thedata is returned and how to handle any errors encountered while evaluating the JSON path expression. The followingexamples show the use of JSON_QUERY.

This module demonstrates how to use the JSON_QUERY operator to extract objects and arrays from a JSON documentusing JSON path expressions.

The first query shows how to return the contents of the key ShippingInstructions:

select JSON_QUERY(PO_DOCUMENT,'$.ShippingInstructions') from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the contents of ShippingInstructions is an object, the value is returned as a JSON object delimited by curly braces"{" and "}".

The second query shows how to return the contents of the key LineItems:

select JSON_QUERY(PO_DOCUMENT ,'$.LineItems') from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the contents of LineItems is an array, the value is returned as a JSON array delimited by square brackets "[" and"]".

By default, for maximum efficiency, objects and arrays are printed with the minimal amount of whitespace. Thisminimizes the number of bytes needed to represent the value, and is fine when the object will be consumed by acomputer. However, sometimes it is necessary for the output of a JSON_QUERY operation to be human readable.JSON_QUERY provides the keyword PRETTY for this purpose.

The third query shows the use of the PRETTY keyword to format the output of the JSON_QUERY operator.

select JSON_QUERY(PO_DOCUMENT ,'$.LineItems' PRETTY) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT,'$.PONumber' returning NUMBER(10)) = 450 /

The array is output as neatly indented JSON, making it much easier for a human to understand. However this comes atthe cost of increasing the number of bytes needed to represent the content of the array.

The fourth query shows how to use an array offset to access one item from an array of objects:

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[0]' PRETTY) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450

Page 13: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

/

Since the selected item is a JSON object, it is delimited by curly braces "{" and "}".

The fifth query shows how to access the value of a key from one specific member of an array of objects.

select JSON_QUERY(PO_DOCUMENT, '$.LineItems[0].Part') from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450/

Since the contents of Part is an object, the value is returned as a JSON object delimited by curly braces "{" and "}".

The JSON_QUERY function also provides options for handling errors that might be encountered when applying theJSON path expression to a JSON document. Options available include:

NULL ON ERROR: The default. If an error is encountered while applying a JSON path expression to a JSONdocument, the result is assumed to be NULL and no error condition is raised.ERROR ON ERROR: An error is raised in the event that an error is encountered while applying a JSON pathexpression to a JSON document.EMPTY ON ERROR: An empty array, "[]", is returned in the event that an error is encountered while applying aJSON path expression to a JSON document.

The most common source of an error with JSON_QUERY is that the JSON path expression resolves to something otherthan an object or array. The NULL ON ERROR behavior ensures that a single error caused by one outlier does not abortan entire operation.

The sixth query shows the default behavior for JSON_QUERY in the event of an error during JSON path evaluation.

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[0].Part.UPCCode') from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

In this example the error is that the JSON path expression maps to a scalar value rather than an object or an array. SinceJSON_QUERY can only return an object or an array, the NULL ON ERROR handling kicks in and results in the queryreturning NULL.

The seventh query shows the ERROR ON ERROR behavior for JSON_QUERY in the event of an error during JSONpath evaluation.

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[0].Part.UPCCode' ERROR ON ERROR) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the result of the JSON path expression is a scalar value, the ERROR ON ERROR handling kicks in and results inan "ORA-40480: result cannot be returned without array wrapper" error being raised.

Page 14: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The eighth query shows the use of the EMPTY ON ERROR behavior

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[0].Part.UPCCode' EMPTY ON ERROR) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the result of the JSON path expression is a scalar value, the EMPTY ON ERROR handling kicks in and the resultof the JSON_QUERY operation is an empty array, [].

The ninth query shows how to use the WITH CONDITIONAL ARRAY WRAPPER option to avoid a JSON_QUERYerror when a JSON path expression evaluates to a scalar value. When this option is specified, a JSON path expressionthat evaluates to a scalar value is returned as an array. The array consists of one element containing a scalar value.

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[0].Part.UPCCode' WITH CONDITIONAL ARRAY WRAPPER) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

Since the result of the JSON path expression is a scalar value, JSON_QUERY automatically converts the result into anarray with that one item and returns the array.

The tenth query shows how to use WITH ARRAY WRAPPER to force JSON_QUERY to always return the result as anarray. In this example, the index is an asterisk, indicating that all members of the LineItems array should be processed,and the last key in the JSON path expression is also an asterisk, indicating that all children of the Part key should beprocessed.

select JSON_QUERY(PO_DOCUMENT,'$.LineItems[*].Part.*' WITH ARRAY WRAPPER) from J_PURCHASEORDER p where JSON_VALUE(PO_DOCUMENT ,'$.PONumber' returning NUMBER(10)) = 450 /

The result of executing this query is an array with 6 items. The first 3 items come from the Description, UnitPrice andUPCCode associated with the Part key of the first member of the LineItems array. The second 3 come from theDescription, UnitPrice and UPCCode associated with the Part key of the second member of the LineItems array. Alsonote that since Description and UPCCode are strings, and UnitPrice is a numeric, the resulting array is heterogeneous innature, containing a mixture of string and numeric values.

5. Relational access to JSON content using JSON_TABLE

The JSON_TABLE operator is used in the FROM clause of a SQL statement. It enables the creation of an inlinerelational view of JSON content. The JSON_TABLE operator uses a set of JSON path expressions to map content froma JSON document into columns in the view. Once the contents of the JSON document have been exposed as columns,all of the power of SQL can be brought to bear on the content of JSON document.

The input to the JSON_TABLE operator is a JSON object or a JSON array. The JSON_TABLE operator is driven by a

Page 15: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

row pattern which consists of a JSON path expression. The row pattern determines how many rows the JSON_TABLEoperator will generate. It will generate 1 row from each key that matches the supplied row pattern. If the row patternmatches a single key then the JSON_TABLE operator will generate exactly one row. If the row pattern references oneor more arrays then it will generate a row from each item in the deepest array.

The rows output by a JSON_TABLE operator are laterally joined to the row that generated them. There is no need tosupply a WHERE clause to join the output of the JSON_TABLE operator with the table containing the JSON document.

Columns are defined by one or more column patterns that appear after the COLUMNS keyword. The inline viewcontains 1 column for each entry in the columns clause. Each column pattern consists of a column name, a SQL datatype and a JSON path expression. The column name determines the SQL name of the column, the SQL data typedetermines the data type of the column and the JSON path expression maps a value from the document to the column.The JSON path expressions in the columns clause are relative to the row pattern.

This module shows how to use JSON_TABLE to generate inline relational views of JSON documents.

The first query shows how to project a set of columns from values that occur at most once in the document. The valuescan come from any level of nesting, as long as they do not come from keys that are part of, or descended from an array,unless an index is used to identify one item in the array.

select M.* from J_PURCHASEORDER p, JSON_TABLE( p.PO_DOCUMENT , '$' columns PO_NUMBER NUMBER(10) path '$.PONumber', REFERENCE VARCHAR2(30 CHAR) path '$.Reference', REQUESTOR VARCHAR2(32 CHAR) path '$.Requestor', USERID VARCHAR2(10 CHAR) path '$.User', COSTCENTER VARCHAR2(16 CHAR) path '$.CostCenter', TELEPHONE VARCHAR2(16 CHAR) path '$.ShippingInstructions.Phone[0].number' ) M where PO_NUMBER between 450 and 455 /

This example generates a view with 5 columns, REFERENCE, REQUESTOR, USERID, COSTCENTER andTELEPHONE. The use of "$" as the JSON path expression for the ROW pattern means the entire JSON document. Thismeans that all column patterns are descended directly from the top level object.

The second query shows how to work with arrays. In order to expose the contents of an array as a set of rows, the arraymust be processed using the NESTED PATH syntax. When a JSON_TABLE operator contains a NESTED PATHclause it will output one row for each member of the array referenced by the deepest NESTED PATH clause. The rowwill contain all of the columns defined by each level of the JSON_TABLE expression.

select D.* from J_PURCHASEORDER p, JSON_TABLE( p.PO_DOCUMENT , '$' columns( PO_NUMBER NUMBER(10) path '$.PONumber', REFERENCE VARCHAR2(30 CHAR) path '$.Reference',

Page 16: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

REQUESTOR VARCHAR2(32 CHAR) path '$.Requestor', USERID VARCHAR2(10 CHAR) path '$.User', COSTCENTER VARCHAR2(16) path '$.CostCenter', NESTED PATH '$.LineItems[*]' columns( ITEMNO NUMBER(16) path '$.ItemNumber', DESCRIPTION VARCHAR2(32 CHAR) path '$.Part.Description', UPCCODE VARCHAR2(14 CHAR) path '$.Part.UPCCode', QUANTITY NUMBER(5,4) path '$.Quantity', UNITPRICE NUMBER(5,2) path '$.Part.UnitPrice' ) ) ) D where PO_NUMBER between 450 and 455 /

The NESTED PATH option simplifies the processing of nested collections. The NESTED PATH clause removes theneed to define columns whose sole purpose is to pass a collection from one operator to the next, making it possible touse a "*" for the select list since the collections are no longer emitted by the JSON_TABLE operator.

Creating Relational views of JSON content

One common use of JSON_TABLE is to create relational views of JSON content which can then be operated on usingstandard SQL syntax. This has the advantage of allowing programmers and tools that have no concept of JSON data andJSON path expressions to work with JSON documents that have been stored in the Oracle Database.

In Oracle 12.1.0.2.0 it highly recommended to avoid joins between these views. A fully expanded detail view willprovide much better performance than defining a master view and a detail view and then attempting to write a query thatreturns a result set consisting of columns selected from both the master view and the detail view.

This module shows how to use JSON_TABLE to create relational views of JSON content that can be queried usingstandard SQL.

The first statement creates a relational view called PURCHASEORDER_MASTER_VIEW which exposes values thatoccur at most once in each document.

create or replace view PURCHASEORDER_MASTER_VIEW as select m.* from J_PURCHASEORDER p, JSON_TABLE( p.PO_DOCUMENT , '$' columns( PO_NUMBER NUMBER(10) path'$.PONumber', REFERENCE VARCHAR2(30 CHAR) path '$.Reference', REQUESTOR VARCHAR2(128 CHAR) path '$.Requestor', USERID VARCHAR2(10 CHAR) path '$.User', COSTCENTER VARCHAR2(16) path '$.CostCenter', SHIP_TO_NAME VARCHAR2(20 CHAR) path '$.ShippingInstructions.name', SHIP_TO_STREET VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.street', SHIP_TO_CITY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.city', SHIP_TO_COUNTY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.county', SHIP_TO_POSTCODE VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.postcode', SHIP_TO_STATE VARCHAR2(2 CHAR) path '$.ShippingInstructions.Address.state', SHIP_TO_PROVINCE VARCHAR2(2 CHAR) path '$.ShippingInstructions.Address.province', SHIP_TO_ZIP VARCHAR2(8 CHAR) path '$.ShippingInstructions.Address.zipCode', SHIP_TO_COUNTRY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.country', SHIP_TO_PHONE VARCHAR2(24 CHAR) path '$.ShippingInstructions.Phones[0].number',

Page 17: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

INSTRUCTIONS VARCHAR2(2048 CHAR) path '$.SpecialInstructions' ) ) m /

As can be seen from the output of a describe operation, this view looks like any other relational view. The JSONoperators and JSON path expressions are hidden away in the DDL statement that created the view.

The second statement creates a relational view called PURCHASEORDER_DETAIL_VIEW that exposes the contentsof the LineItems array as a set of rows.

create or replace view PURCHASEORDER_DETAIL_VIEW as select D.* from J_PURCHASEORDER p, JSON_TABLE( p.PO_DOCUMENT , '$' columns ( PO_NUMBER NUMBER(10) path'$.PONumber', REFERENCE VARCHAR2(30 CHAR) path '$.Reference', REQUESTOR VARCHAR2(128 CHAR) path '$.Requestor', USERID VARCHAR2(10 CHAR) path '$.User', COSTCENTER VARCHAR2(16) path '$.CostCenter', SHIP_TO_NAME VARCHAR2(20 CHAR) path '$.ShippingInstructions.name', SHIP_TO_STREET VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.street', SHIP_TO_CITY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.city', SHIP_TO_COUNTY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.county', SHIP_TO_POSTCODE VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.postcode', SHIP_TO_STATE VARCHAR2(2 CHAR) path '$.ShippingInstructions.Address.state', SHIP_TO_PROVINCE VARCHAR2(2 CHAR) path '$.ShippingInstructions.Address.province', SHIP_TO_ZIP VARCHAR2(8 CHAR) path '$.ShippingInstructions.Address.zipCode', SHIP_TO_COUNTRY VARCHAR2(32 CHAR) path '$.ShippingInstructions.Address.country', SHIP_TO_PHONE VARCHAR2(24 CHAR) path '$.ShippingInstructions.Phones[0].number', INSTRUCTIONS VARCHAR2(2048 CHAR) path '$.SpecialInstructions' NESTED PATH '$.LineItems[*]' columns ( ITEMNO NUMBER(38) path '$.ItemNumber', DESCRIPTION VARCHAR2(256 CHAR) path '$.Part.Description', UPCCODE VARCHAR2(14 CHAR) path '$.Part.UPCCode', QUANTITY NUMBER(12,4) path '$.Quantity', UNITPRICE NUMBER(14,2) path '$.Part.UnitPrice' ) ) ) D /

The following statements show how, once the relational views have been created, the full power of SQL can now beapplied to JSON content, without requiring any knowledge of the structure of the JSON or how to manipulate JSONusing SQL.

Relation queries with simple predicates

select SHIP_TO_STREET, SHIP_TO_CITY, SHIP_TO_STATE, SHIP_TO_ZIP from PURCHASEORDER_MASTER_VIEW where PO_NUMBER = 450 /select PO_NUMBER, REFERENCE, SHIP_TO_PHONE, DESCRIPTION, QUANTITY, UNITPRICE

Page 18: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

from PURCHASEORDER_DETAIL_VIEW where UPCCODE = '27616854773' /select PO_NUMBER, REFERENCE, SHIP_TO_PHONE, QUANTITY, DESCRIPTION, UNITPRICE from PURCHASEORDER_DETAIL_VIEW where UPCCODE in ('27616854773', '56775053895', '75993852820') order by PO_NUMBER /

Relational group by queries

select PO_NUMBER, REFERENCE, SHIP_TO_PHONE, QUANTITY, DESCRIPTION, UNITPRICE from PURCHASEORDER_DETAIL_VIEW where UPCCODE in ('27616854773', '56775053895', '75993852820') order by PO_NUMBER /select COSTCENTER, count(*) from PURCHASEORDER_MASTER_VIEW group by COSTCENTER order by COSTCENTER /select COSTCENTER, sum (QUANTITY * UNITPRICE) TOTAL_VALUE from PURCHASEORDER_DETAIL_VIEW group by COSTCENTER /

Relational queries with multiple predicates

select PO_NUMBER, REFERENCE, INSTRUCTIONS, ITEMNO, UPCCODE, DESCRIPTION, QUANTITY, UNITPRICE from PURCHASEORDER_DETAIL_VIEW d where REQUESTOR = 'Steven King' and QUANTITY > 7 and UNITPRICE > 25.00 /

SQL Analytics

select UPCCODE, count(*) "Orders", Quantity "Copies" from PURCHASEORDER_DETAIL_VIEW where UPCCODE in ('27616854773', '56775053895', '75993852820') group by rollup(UPCCODE, QUANTITY) /select UPCCODE, PO_NUMBER, REFERENCE, QUANTITY, QUANTITY - LAG(QUANTITY,1,QUANTITY) over (ORDER BY PO_NUMBER) as DIFFERENCE from PURCHASEORDER_DETAIL_VIEW where UPCCODE = '27616854773' order by PO_NUMBER DESC /

Relational views effectively provide schema-on-query semantics; making it possible to define multiple views of theJSON content. Application developers are still free to evolve the content of the JSON as required. New variants of theJSON can be stored without affecting applications that rely on the existing views.

As can be seen from the queries used, all the power of the SQL language can be used to access the JSON data. These

Page 19: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

views allow developers and, more importantly, tools that understand only the relational paradigm to work with JSONcontent.

6. Filtering result sets using JSON_EXISTS

The JSON_EXISTS operator is used in the WHERE clause of a SQL statement. It is used to test whether or not a JSONdocument contains content that matches the provided JSON path expression.

The JSON_EXISTS operator takes two arguments, a JSON column and a JSON path expression. It returns TRUE if thedocument contains a key that matches the JSON path expression, FALSE otherwise. JSON_EXISTS provides a set ofmodifiers that provide control over how to handle any errors encountered while evaluating the JSON path expression.

The following examples show the use of JSON_EXISTS.The first query counts the number of JSON documents thatcontain a ShippingInstructions key with an Address key that contains a state key:

select count(*) from J_PURCHASEORDER where JSON_EXISTS(PO_DOCUMENT ,'$.ShippingInstructions.Address.state') /

JSON_EXISTS makes it possible to differentiate between a document where a key is not present and a document wherethe key is present but the value is null. The next three queries show the difference between using JSON_EXISTS to testfor the presence of a key and using JSON_VALUE to check if a key is null or empty.

The first query simply shows the set of possible values for the county key in the Address object.

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstructions.Address.county'), count(*) from J_PURCHASEORDER group by JSON_VALUE(PO_DOCUMENT,'$.ShippingInstructions.Address.county') /

The results show that there are two possible values for the county key; it is either NULL or contains the value "Oxon.".The problem with this is that, for the NULL values, we cannot differentiate between cases where JSON_VALUEreturned NULL because the key did not exist in the document and the cases where it returned NULL because the keywas present but the value contains a null or empty value.

With JSON_EXISTS it is possible to differentiate between the case where the key is not present and the case where thekey has a null or empty value. The following query shows how this is done:

select JSON_VALUE(PO_DOCUMENT,'$.ShippingInstructions.Address.county'), count(*) from J_PURCHASEORDER where JSON_EXISTS(PO_DOCUMENT ,'$.ShippingInstructions.Address.county') group by JSON_VALUE(PO_DOCUMENT,'$.ShippingInstructions.Address.county') /

The results show that there are 13 documents where the key is present but empty.

Predicate support in JSON Path Expressions

Page 20: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

Starting with Oracle Database 12c Release 2, The JSON path expression used with JSON_EXISTS support predicates.This significantly extends the power of JSON_EXISTS by enabling JSON_PATH expressions to include conditions onthe value of the key, as well as the presence of a key.

Predicates are specified by adding a "?" following the parent key and then specifying conditions in parenthesis. Withinthe predicate the @ sign is used to reference the parent key. A variable is indicated by a name prefixed with a $ sign.The value of the variable is set using the PASSING clause of the JSON_EXISTS operator. This allows the value tosupplied using a bind variable when developing applications.

The following statement shows a very simple example of using a predicate in a JSON path Expression/ In this examplea predicate is placed on the value of the PONumber key. PONumber is a member of the top level JSON object.

select j.PO_DOCUMENT from J_PURCHASEORDER j where JSON_EXISTS( PO_DOCUMENT, '$?(@.PONumber == $PO_NUMBER)' passing 450 as "PO_NUMBER" ) /

The predicate is this case is "@.PONumber == $PO_.NUMBER". Since the PONumber key is part of the top-levelobject the JSON path expression in this example starts with a "$". The presence of the predicate is indicated by adding "?()" to the JSON path expression. The predicate is then placed inside the parenthesis.

The value of the variable PO_NUMBER is supplied using the PASSING clause of the JSON_EXISTS operator.

In the next example a predicate is placed on the value of the UPCCode key. UPCCode occurs inside Part which in turnis one of the keys found in the objects that make up the LineItems array.

select j.PO_DOCUMENT.PONumber PO_NUMBER from J_PURCHASEORDER j where JSON_EXISTS( PO_DOCUMENT, '$?(@.LineItems.Part.UPCCode == $UPC)' passing 27616854773 as "UPC" )/

The predicate in this case is "@.LineItems.Part.UPCode == $UPC". Since LineItems contains an array, the conditionwill be met if any member of the array contains a Part object with a UPCCode key whose value matches the specifiedcondition.

The next statement shows how to provide multiple predicates and how to perform a numeric range query. The firstcondition is on the value of the User key. User is member of the top-level object. The second condition is on the valueof the Quantity key. Quantity occurs in each of the objects that make up the array LineItems. The two conditions arelinked using the "&&" operator. The ">" conditional in the second condition indicates that a range search is required.

select count(*) from J_PURCHASEORDER j where JSON_EXISTS(

Page 21: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

PO_DOCUMENT, '$?(@.User == $USER && @.LineItems.Quantity > $QUANTITY)' passing 'AKHOO' as "USER", 8 as "QUANTITY" ) /

In this example the first predicate contains two conditions.

The first condition is on the value of the User key. The second condition is an existence check on the contents of theLineItems array. The two conditions are linked using the "&&" operator.

The exists operator is used when working with arrays. The exists operation returns true if a single member of the arraycontains an object that satisfies all of the conditions specified by the supplied path expression. It returns false otherwise.

Without the exists operator the document could be selected even though no single member of the array satisfies all theconditions specified in the path expression. As long as each of the individual conditions specified in the path expressionis satisfied by at least one member of the array the document would be selected.

The above example uses a nested predicate to check if any member of the array contains a Part object where the value ofUPCCode key matches the value specified using the variable UPC and a Quantity key whose value is greater than thevalue specified using the variable QUANTITY. If any member of the array satisfies the supplied predicates the existsoperator returns true and the document is included in the results set.

select j.PO_DOCUMENT.PONumber PO_NUMBER from J_PURCHASEORDER j where JSON_EXISTS( PO_DOCUMENT, '$?( @.User == $USER && exists( @.LineItems?(@.Part.UPCCode == $UPC && @.Quantity > $QUANTITY) ) )' passing 'AKHOO' as "USER", 17153100211 as "UPC", 8 as "QUANTITY" ) /

7. Indexing JSON documents stored in Oracle Database 12c

Oracle Database 12c support multiple ways of indexing JSON documents. There are two approaches to indexing JSONcontent. The first is to create functional indexes based on the JSON_VALUE operator. The second is to leverageOracle"s full-text indexing capabilities to create an inverted list index on the JSON documents.

Indexing scalar values using JSON_VALUE

JSON_VALUE can be used to create an index on any key in a JSON document as long as the following two conditionsare met.

1. The target of the JSON path expression must be a scalar value.2. The target of the JSON path expression must occur at most once in each document.

Indexes created using JSON_VALUE can be conventional B-TREE indexes or BITMAP indexes.

Page 22: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The first statement shows how to create a unique B-Tree index on the contents of the PoNumber key.

create unique index PO_NUMBER_IDX on J_PURCHASEORDER ( JSON_VALUE( PO_DOCUMENT,'$.PONumber' returning NUMBER(10) ERROR ON ERROR NULL ON EMPTY ) ) /

As can be seen when a query includes a predicate on the expression that was used to create the index the index is used tosatisfy the predicate

set autotrace on explain--select PO_DOCUMENT from J_PURCHASEORDER j where JSON_VALUE(PO_DOCUMENT,'$.PONumber' returning NUMBER(10) ERROR ON ERROR ) = 123 /

The second statement shows how to create a BITMAP index on the contents of the CostCenter key

create bitmap index COSTCENTER_IDX on J_PURCHASEORDER( JSON_VALUE(PO_DOCUMENT ,'$.CostCenter') ) /

The third statement shows how to create a B-Tree index on the contents of the zipCode key

create index ZIPCODE_IDX on J_PURCHASEORDER( JSON_VALUE( PO_DOCUMENT, '$.ShippingInstructions.Address.zipCode' returning NUMBER(5) ) ) /

Note the use of the returning clause in the JSON_VALUE operator to create a numeric index.

8. JSON Search Index

In addition to creating index on specific nodes, Oracle Database 12c also supports creating a JSON Search Index. AJSON Search Index allows a single index to optimize JSON Path operations on any key. A JSON search index requiredno advanced knowledge of structure of the JSON documents that are being indexed, or the JSON path expressions thatwill be used when searching.

In Oracle Database 12c release the DDL statements required to create a JSON search index have been significantly

Page 23: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

simplified. The following statement will create the JSON Search Index.

create search Index JSON_SEARCH_INDEX on J_PURCHASEORDER (PO_DOCUMENT) for json/

The new CREATE SEARCH INDEX syntax makes creating a JSON Search Index much more like creating any otherkind of index. It is no longer necessary to create preferences, and provide information about section groups and lexarswhen creating a search index. The parameters clause may still be used to override the default settings of certainconfigurable options, such as when to sync the index.

As well as being much easier to create, in Oracle Database 12c Release 2 the JSON search index has a number ofsignificant enhancements

It add support for range-based searching on numeric values.It is transactional (SYNC on COMMIT) by default.It support RANGE, LIST, INTERVAL and HASH partitioned tables

These enhancements make it a much more general solution for indexing JSON Documents

9. JSON Data Guide

The JSON Data Guide allows you to discover information about the structure and content of JSON documents stored inOracle Database. This information can be used in a number of ways including

1. Generating a JSON Schema document that describes the set of JSON documents2. Creating views that enable relational SQL operations on the JSON documents3. Automatically adding virtual columns based on simple key:value pairs

In order to generate a JSON data guide we need to perform an analysis of the JSON content that the database ismanaging. The following statements show how to create a data guide on a set of JSON documents that have been storedin an Oracle Database, and how to use the data guide to discover metadata about the documents stored in the database. Italso shows how to use the data guide to create relational views that expose the content of the JSON documents in amanner that enables SQL-based reporting and analytics.

The first statement shows how to create the data guide. The data guide is based on the same infrastructure as the JSONsearch index, so the syntax used to create a database guide is similar to the syntax used to create a search index.

drop index JSON_SEARCH_INDEX / create search index JSON_SEARCH_INDEX on J_PURCHASEORDER (PO_DOCUMENT) for json parameters('search_on none data guide on') /

This example creates a basic JSON data guide that will track of all the keys used by all the documents stored in thePO_DOCUMENT column of table J_PURCHASEORDER.

Page 24: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

Since the data guide is based on the JSON search infrastructure we have the option of using a single index to drive boththe data guide and search operations. However if we only require the data guide functionality we can avoid the overheadassociated with maintaining the search index by using the parameters clause of the create search index statement tospecify the options "search_on none" and "data guide on".

Once the JSON data guide has been created, we can use it to understand the structure of the JSON documents that havebeen stored in the column that has been indexed. A new PL/SQL package, DBMS_JSON, has been introduced to assistwith this.

The method getDataGuide() allows us to get an overview of the keys present in the JSON documents. The method canreturn 2 representations of the information. The first uses JSON Schema (http://json-schema.org/documentation.html) torepresent the information. The second provides a relational view of the data showing all of the possible JSON pathexpressions that can be used to access the content stored in the JSON documents. Both representations also provideinformation about the data type and size of the value associated with the JSON path expression.

The next statement shows how to use the getDataGuide method to generate a JSON Schema document that describes thedocuments analyzed by the data guide.

select DBMS_JSON.GET_INDEX_DATAGUIDE( SYS_CONTEXT('USERENV','CURRENT_USER'), 'J_PURCHASEORDER', 'PO_DOCUMENT', DBMS_JSON.FORMAT_HIERARCHICAL, DBMS_JSON.PRETTY ) "HIERARCHICAL DATA GUIDE" from dual /

The next statement shows how to request the flattened representation of the data guide.

select DBMS_JSON.GET_INDEX_DATAGUIDE( SYS_CONTEXT('USERENV','CURRENT_USER'), 'J_PURCHASEORDER', 'PO_DOCUMENT', DBMS_JSON.FORMAT_FLAT, DBMS_JSON.PRETTY ) "FLAT DATA GUIDE" from dual /

The next statement shows how to use the create_view_on_path procedure to create a relational view based on theinformation in the data guide The procedure takes 4 arguments: The name of the view, the name of the underlying table,the name of the column and a JSON path expression that identifies which JSON path expressions are to be included inthe generated view.

begin DBMS_JSON.CREATE_VIEW_ON_PATH( 'J_PURCHASEORDER_VIEW', 'J_PURCHASEORDER', 'PO_DOCUMENT',

Page 25: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

'$' ); end;/

In this example the JSON path expression supplied to the procedure is "$". This means that the view will contain onecolumn for each JSON path expression that can occur in the set of documents that have been analyzed. The view willexpand all possible nested collections and contain one row for each member of the deepest nested arrays. If thedocument contains more than one hierarchy, right outer join logic is used to ensure that each of the hierarchies present isrepresented by rows in the view.

The view generated by createViewOnPath is a normal relational view that can be examined using as SQL*Plus describecommand

--desc J_PURCHASEORDER_VIEW --

The names of the columns in the view are generated from the JSON path expressions that provide the content of thecolumn. Name mangling will occur when the path expression cannot be converted directly into a valid SQL columnname. This can lead to some less than intuitive column names. In order to avoid this, package DBMS_JSON provides aprocedure called rename that can be used to associate a user-friendly name with a JSON path expression.

The next statement shows how to use the rename procedure to provide user-friendly names for some of the columnsderived from the PurchaseOrder documents. The procedure takes five arguments, the name of the table and column thatcontains the JSON documents, the JSON path expression, the data type that should be used for the value identified bythe JSON path expression, and the name that should be used for any column based on the JSON path.

begin DBMS_JSON.RENAME_COLUMN('J_PURCHASEORDER','PO_DOCUMENT','$.PONumber',DBMS_JSON.TYPE_NUMBER,'PO_NUMBER'); DBMS_JSON.RENAME_COLUMN( 'J_PURCHASEORDER','PO_DOCUMENT','$.Reference',DBMS_JSON.TYPE_STRING,'REFERENCE'); DBMS_JSON.RENAME_COLUMN( 'J_PURCHASEORDER','PO_DOCUMENT','$.LineItems.Part.UPCCode',DBMS_JSON.TYPE_NUMBER, 'UPC_CODE'); end; /

Once user-friendly names have been associated with the JSON path expressions, creating or recreating the view usingprocedure createViewOnPath will result in the creation of views which use the user-friendly column names.

begin DBMS_JSON.CREATE_VIEW_ON_PATH('J_PURCHASEORDER_VIEW', 'J_PURCHASEORDER', 'PO_DOCUMENT', '$' ); end; /-- desc J_PURCHASEORDER_VIEW--

The next statement shows that views created using this technique function just like any other relational view. Thisallows the full power of the Oracle SQL language to be used to query JSON content and ensure that tools andprogrammers who only understand relational techniques can work with content stored as JSON in an Oracle database.

Page 26: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

select REFERENCE from J_PURCHASEORDER_VIEW where PO_NUMBER between 500 and 504/

The next example shows how procedure createViewOnPath can also generate a file containing the SQL DLL statementsrequired to create the view. The file is stored in the XML DB repository.

drop view J_PURCHASEORDER_VIEW/ begin if DBMS_XDB.existsResource('/public/J_PURCHASEORDER_VIEW.sql') then DBMS_XDB.deleteResource('/public/J_PURCHASEORDER_VIEW.sql'); end if; commit; end; / begin DBMS_JSON.CREATE_VIEW_ON_PATH( VIEWNAME => 'J_PURCHASEORDER_VIEW', TABLENAME => 'J_PURCHASEORDER', JCOLNAME => 'PO_DOCUMENT', PATH => '$', RESOURCEPATH => '/public/J_PURCHASEORDER_VIEW.sql' ); end;/

The next statement shows how to the access the content of the file using the XDBURITYPE function.

select xdburitype('/public/J_PURCHASEORDER_VIEW.sql').getClob() "J_PURCHASEORDER_VIEW DDL STATEMENT" from DUAL/

The file containing the DDL can also be accessed using the database"s HTTP, WebDAV and FTP servers.

JSON Data Guide and virtual columns

Virtual columns can also be used to expose JSON content in a relational manner. The Virtual Columns are appended tothe table that includes the column that contains the JSON content. Each Virtual Column can expose the value identifiedby a JSON path expression, as long as the JSON path expression identifies a key that can occur at most once in eachdocument.

This section of the Hands-on-Lab shows how to create a JSON data guide that automatically appends Virtual Columnsto a table containing JSON documents as new JSON path expressions are detected by the data guide.

The following statements show how to use a data guide that will expose JSON content using Virtual Columns.

The first statement shows the current definition of the J_PURCHASEORDER table

Page 27: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

-- desc J_PURCHASEORDER --

The second statement shows how to modify the parameters clause of the create search index statement to create a JSONsearch index that will automatically add Virtual columns to the base table. Note the use of the "on change add_vc"clause rather than the "data guide on" clause.

DROP INDEX JSON_SEARCH_INDEX / create search index JSON_SEARCH_INDEX on J_PURCHASEORDER (PO_DOCUMENT) for json parameters('sync (on commit) SEARCH_ON NONE DATAGUIDE ON CHANGE ADD_VC')/

The third statement shows that after the data guide has been created the base table has been modified. A Virtual Columnhas been appended to the base table for each JSON path expression that references a leaf level key that occurs no morethan once per document.

-- desc J_PURCHASEORDER --

This technique cannot be used with keys that are occur within an array, so in this example no columns are generated forJSON path expressions that include the keys Phone and LineItems, since the content of these items are arrays.

The fourth statement shows that you can use SQL to query the Virtual Columns added by the data guide just like anyother column in the table

select "PO_DOCUMENT$PONumber", "PO_DOCUMENT$User" from J_PURCHASEORDER where "PO_DOCUMENT$PONumber" between 500 and 504/

The fifth statement shows what happens when you insert a JSON document that contains keys that were not present inany of the documents stored in the table when the data guide was created.

insert into J_PURCHASEORDER (ID, DATE_LOADED, PO_DOCUMENT) values (SYS_GUID(),SYSTIMESTAMP,'{"NewKey1":1,"NewKey2":"AAAA"}') / commit/

As can be seen the insert succeeded, as would be expected since the document contained valid JSON. However, if wedescribe the table after the insert has been committed, and after the index is synchronized (which happens automatically

Page 28: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

since the "sync (on commit)" option was specified as one of the parameters passed to the create search index statement),we see that the data guide has automatically appended Virtual Columns corresponding to the new keys to the base table.

-- desc J_PURCHASEORDER --

The last statement shows that the new columns can be queried just like any other columns.

select "PO_DOCUMENT$NewKey1" KEY, "PO_DOCUMENT$NewKey2" VALUE from J_PURCHASEORDER where "PO_DOCUMENT$NewKey1" = 1 /

10. JSON Generation

Oracle Database 12c Release 2 includes support for new SQL/JSON operators that allow JSON documents to begenerated directly from a SQL statement without having to resort to error-prone string concatenation techniques. Theseoperators are

JSON_ARRAYJSON_OBJECTJSON_ARRAYJSON_ARRAYAGGJSON_OJBECTAGG

The first statement shows how to use JSON_ARRAY. JSON_ARRAY returns each row of data generated by the SQLquery as a JSON Array. The input to JSON_ARRAY is a list of columns. Each column will be mapped to one memberof the array.

select JSON_ARRAY( DEPARTMENT_ID,DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID ) DEPARTMENT from HR.DEPARTMENTS where DEPARTMENT_ID < 110/

The second statement shows how to use JSON_OBJECT. JSON_OBJECT returns each row of data generated by theSQL Query as a JSON object. The generated object contains a key:value pair for each column referenced in theJSON_OBJECT operator. The name of the key can be supplied using a SQL string or a column.

select JSON_OBJECT( 'departmentId' IS d.DEPARTMENT_ID, 'name' IS d.DEPARTMENT_NAME, 'manager' IS d.MANAGER_ID,'location' IS d.LOCATION_ID ) DEPARTMENT from HR.DEPARTMENTS d where DEPARTMENT_ID < 110/

Page 29: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The third statement shows how a column name can be used generate the name of the key from the value of the column

select JSON_OBJECT(OBJECT_TYPE is OBJECT_NAME,'Status' is STATUS) from USER_OBJECTS where ROWNUM < 10/

The fourth statement shows how to nest JSON_OBJECT operators to generate nested JSON structures. In this examplea nested JSON_OBJECT operator is used to generate the object associated with the manager key. The object containsinformation about the department's manager.

SELECT JSON_OBJECT( 'departmentId' IS d.DEPARTMENT_ID, 'name' IS d.DEPARTMENT_NAME, 'manager' is JSON_OBJECT( 'employeeId' is EMPLOYEE_ID, 'firstName' is FIRST_NAME, 'lastName' is LAST_NAME, 'emailAddress' is EMAIL ), 'location' is d.LOCATION_ID ) DEPT_WITH_MGR from HR.DEPARTMENTS d, HR.EMPLOYEES e where d.MANAGER_ID is not null and d.MANAGER_ID = e.EMPLOYEE_ID and d.DEPARTMENT_ID = 10/

The fifth statement shows how to use the operator JSON_ARRAYAGG to generate JSON arrays from the results of asub query that returns multiple rows. In this example we are showing the employees for each department as a JSONarray inside the department document.

select JSON_OBJECT( 'departmentId' is d.DEPARTMENT_ID, 'name' is d. DEPARTMENT_NAME, 'employees' is ( select JSON_ARRAYAGG( JSON_OBJECT( 'employeeId' is EMPLOYEE_ID, 'firstName' is FIRST_NAME, 'lastName' is LAST_NAME, 'emailAddress' is EMAIL ) ) from HR.EMPLOYEES e where e.DEPARTMENT_ID = d.DEPARTMENT_ID ) ) DEPT_WITH_EMPLOYEES from HR.DEPARTMENTS d where DEPARTMENT_NAME = 'Executive'/

The sixth statement shows the contents of view EMPLOYEE_KEY_VALUE. This view stores information aboutemployees using a Key:Value pair storage model.

Page 30: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

select ID, KEY, VALUE from EMPLOYEE_KEY_VALUE where ID < 105 order by ID, KEY/

The seventh statement shows the use of JSON_OBJECTAGG. JSON_OBJECTAGG makes it easy to generate a JSONobject from data that has been stored using Key, Value pair storage.

select JSON_OBJECTAGG(KEY,VALUE) from EMPLOYEE_KEY_VALUE where ID < 105 group by ID/

11. PL/SQL Integration: Introducing the PL/SQL API for JSON

Oracle Database 12c Release 2 provides new PL/SQL APIs for manipulating JSON documents stored in an OracleDatabase. These APIs make it possible to add, update and remove key:value pairs. The APIs allow you to navigate thestructure of a JSON document, in a manner that is very similar to the way in which the XML Document Object Model(DOM) enables navigation of an XML document.

The PL/SQL JSON API consists of three PL/SQL objects, JSON_ELEMENT_T, JSON_OBJECT_T andJSON_ARRAY_T. JSON_OJBECT_T and JSON_ARRAY_T extend JSON_ELEMENT_T, so they inherit all ofJSON_ELEMENT_T"s methods.

Any JSON document can be represented by combining instances of these objects. All PL/SQL operators that returnJSON content return an instance of JSON_ELEMENT_T. The JSON_ELEMENT_T instance can be cast to eitherJSON_OBJECT_T or JSON_ARRAY_T before further operations are performed on the data.

Let's start of by taking a look at the new PL/SQL objects that are used when processing JSON documents.

-- desc JSON_ELEMENT_T --

JSON_ELEMENT_T provides a number of important methods. The most important are PARSE and STRINGIFY.PARSE is used to convert textual JSON into an instance of JSON_ELEMENT_T. STRINGIFY is used to generatetextual JSON from a PL/SQL JSON_ELEMENT_T object.

JSON_ELEMENT_T also provides a set of methods for extracting the value part of a key:value pair as a PL/SQL datatype, and methods for determining what the actual concrete type of any given instance of JSON_ELEMENT_T is.

Now let's take a look at the PL/SQL object JSON_OBJECT_T

-- desc JSON_OBJECT_T --

Page 31: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

In addition to the methods inherited from JSON_ELEMENT_T, JSON_OBJECT_T provides methods for manipulatingthe keys associated with a JSON object. This includes getters and setters for all of the data types supported by the APIand methods for listing, adding, removing and renaming keys.

Next we'll look at the PL/SQL object JSON_ARRAY_T.

-- desc JSON_ARRAY_T --

In addition to the methods inherited from JSON_ELEMENT_T, JSON_ ARRAY _T provides methods for working withthe contents of a JSON array. This includes methods for counting the number of items in an array, accessing an item ofthe array, adding an item to the array and removing an item from the array.

Using the PL/SQL API for JSON

The next section of the Hands-on-Lab shows how we can use the PL/SQL API for JSON to access and update JSONcontent stored in Oracle Database 12c Release 2 without having to resort to complex and error-prone stringmanipulation techniques. In this example we are going to update the value of the UPCCode key for any object where thecurrent value of the UPCCode key is '17153100211' and add a DateModified key to the affected objects.

This code sample shows how to use the new PL/SQL API for JSON to access and update JSON content stored in anOracle Database.

The first statement uses JSON_TABLE to show the current values of the keys PONumber, ItemNumber, andDateModified from documents where the LineItems array contains a object with a UPCCode key whose value is17153100211. Since the UPCCode predicate is specified as a SQL condition on the output of the JSON table operatoronly rows generated from objects which satisfy the UPCCode condition are included in the results set.

select LI.* from J_PURCHASEORDER j, JSON_TABLE( J.PO_DOCUMENT, '$' columns( PO_NUMBER NUMBER(5) PATH '$.PONumber', NESTED PATH '$.LineItems[*]' columns( ITEM_NUMBER NUMBER(4) PATH '$.ItemNumber', UPC_CODE VARCHAR2(14) PATH '$.Part.UPCCode', UPDATED VARCHAR2(38) PATH '$.Part.DateModified' ) ) ) LI where UPC_CODE = '17153100211' /

The next statement shows a PL/SQL block that performs the following operations

1. Open a cursor that locates the documents to be updated and loop over the selected documents. The cursor usesJSON_EXISTS to locate documents that contain a LineItems element with the required UPC.

Page 32: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

2. Use the PARSE method on JSON_ELEMENT_T to parse the JSON document. This returns an instance ofJSON_ELEMENT_T.

3. Use the TREAT operator to cast the result as an instance of JSON_OBJECT_T.4. Use the GET method on JSON_OBJECT_T to get the content of the LineItems key. This returns an instance of

JSON_ELEMENT_T.5. Check that the object returned by the GET method is a JSON array.6. Cast the result as an instance of JSON_ARRAY_T.7. Loop over the items in the array. Use the COUNT property to determine the number of items in the array.8. Use the GET method on JSON_ARRAY_T to access each item in the array. Note that the first item in the array

has an index of 0.9. Check that the object returned by the GET method is a JSON object

10. Use the TREAT operator to cast the result as an instance of JSON_OBJECT_T11. Use the GET method on JSON_OBJECT_T to get the content of the Part key. This returns an instance of

JSON_ELEMENT_T. Use the TREAT operator to cast this value as an instance of JSON_OBJECT_T.12. Use the GETSTRING method on JSON_OBJECT to get the value of the UPCCode key.13. Check if the value of the UPCCode key matches the value we are searching for14. Use the PUT method on JSON_OBJECT_T to update the value of the UPCCode key15. Use the PUT method on JSON_OBJECT_T to create or update the DateModified key16. After all the elements in the array have been processed use the STRINGIFY method on the top level object to

serialize the in-memory representation of the document as textual JSON.17. Update the on-disk representation of the document

declare cursor getDocuments is select PO_DOCUMENT from J_PURCHASEORDER j where JSON_EXISTS( PO_DOCUMENT,'$?(@.LineItems.Part.UPCCode == $UPC)' passing 17153100211 as "UPC" ) for UPDATE; V_RESULT JSON_ELEMENT_T; V_DOCUMENT_OBJECT JSON_OBJECT_T; V_LINEITEMS_ARRAY JSON_ARRAY_T; V_LINEITEM_OBJECT JSON_OBJECT_T; V_PART_OBJECT JSON_OBJECT_T; V_NEW_DOCUMENT VARCHAR2(4000); begin for doc in getDocuments loop V_RESULT := JSON_ELEMENT_T.parse(doc.PO_DOCUMENT); V_DOCUMENT_OBJECT := treat(V_RESULT as JSON_OBJECT_T); V_RESULT := V_DOCUMENT_OBJECT.get('LineItems'); if (V_RESULT.is_array()) then V_LINEITEMS_ARRAY := treat ( V_RESULT as JSON_ARRAY_T); for i in 1..V_LINEITEMS_ARRAY.GET_SIZE() loop V_RESULT := V_LINEITEMS_ARRAY.get(i-1); if (V_RESULT.is_Object()) then V_LINEITEM_OBJECT := treat (V_RESULT as JSON_OBJECT_T); V_PART_OBJECT := treat(V_LINEITEM_OBJECT.get('Part') as JSON_OBJECT_T); if (V_PART_OBJECT.get_String('UPCCode') = '17153100211') then V_PART_OBJECT.put('UPCCode','9999999999'); V_PART_OBJECT.put('DateModified',to_char(SYSTIMESTAMP,'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM')); end if; end if; end loop; end if; V_NEW_DOCUMENT := V_DOCUMENT_OBJECT.stringify(); update J_PURCHASEORDER set PO_DOCUMENT = V_NEW_DOCUMENT where current of getDocuments; end loop; commit; end; /

Page 33: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

The fourth statement shows that after the PL/SQL block has executed no documents exist where the UPCCode keycontains the value 17153100211

select count(*) from J_PURCHASEORDER j where JSON_EXISTS( PO_DOCUMENT,'$?(@.LineItems.Part.UPCCode == $UPC)' passing 17153100211 as "UPC" ) /

The final statement shows that the objects that satisfied the predicate UPCCODE = 17153100211 now satisfy thepredicate UPCCODE = 9999999999 and that each of these objects now has a value associated with the keyDateModified.

select LI.* from J_PURCHASEORDER j, JSON_TABLE( J.PO_DOCUMENT, '$' columns( PO_NUMBER NUMBER(5) PATH '$.PONumber', NESTED PATH '$.LineItems[*]' columns( ITEM_ NUMBER NUMBER(4) PATH '$.ItemNumber', UPC_CODE VARCHAR2(14) PATH '$.Part.UPCCode', UPDATED VARCHAR2(38) PATH '$.Part.DateModified' ) ) ) LI where UPC_CODE = '9999999999' /

12. Spatial Integration and Support for GeoJSON

GeoJSON is a popular standard for representing spatial information in JSON documents. Oracle Database 12c Release 2provides an extension to the JSON_VALUE operator that make it possible to use Oracle Database"s powerful spatialfeatures on JSON content containing information encoded using the GeoJSON standard. This allows location basedqueries to be performed on JSON documents.

In the following example, table CITY_LOT_FEATURES contains information about San Francisco city lots, includingtheir coordinates. The coordinates are specified using GeoJSON. We'll start by creating a table containing FEATUREdocuments from the contents of the cityLots.json file.

First create a table with JSON Documents that include GeoJSON encoded information.

drop table J_PURCHASEORDER/ drop table CITY_LOT_FEATURES / create table CITY_LOT_FEATURES as select JSON_DOCUMENT as FEATURE from JSON_TABLE( xdburitype('/public/tutorials/json/testdata/cityLots.json').getClob(), '$[*]' columns (

Page 34: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

JSON_DOCUMENT VARCHAR2(32000) FORMAT JSON PATH '$' ) ) where rownum < 1000 /

Let start by looking at the details of a feature object.

select JSON_QUERY(FEATURE, '$' PRETTY) from CITY_LOT_FEATURES where JSON_EXISTS( FEATURE, '$?(@.properties.MAPBLKLOT == $LOT)' passing '0001001' as "LOT" ) /

As can be seen a "Feature" consists of a properties object that contains information that describes the feature and aGeoJSON "geometry" object that, in this example, consists of a set of coordinates that define a polygon that identifieswhere the feature is located.

The remaining code snippets show how to query and index JSON documents that contain GeoJSON content.

The first statement shows how to use JSON_VALUE to extract the GeoJSON information as an OracleSDO_GEOMETRY object.

select JSON_VALUE( FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR ) from CITY_LOT_FEATURES where JSON_EXISTS( FEATURE, '$?(@.properties.MAPBLKLOT == $LOT)' passing '0001001' as "LOT" ) /

In Oracle Database 12c release 2 JSON_VALUE has been extended to support using Oracle Spatial to convertGeoJSON encoded location information into SDO_GEOMETRY objects. Generating SDO_GEOMETRY objects fromGeoJSON allows all of the Oracle Spatial functionality to be used on this kind of content.

The second statement shows how this allows the creation of spatial indexes on GeoJSON content.

create index FEATURE_GEO_INDEX on CITY_LOT_FEATURES( JSON_VALUE( FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR ) ) indextype is mdsys.spatial_index /

The third statement shows how we can use Oracle Spatial operators to query GeoJSON content. In this example wereturn the GeoJSON that describes any objects that are within 50 meters of the chosen object.

Page 35: SQL/JSON Features in Database 12 - Oracle · SQL/JSON Features in Database 12.2 ... Oracle Database 12c includes new SQL operators that allow the ... Accessing JSON content using

file:///C/Users/Mark%20D%20Drake/Desktop/JSON/Whitepapers/HOLInstructions%20[PDFSource].html[12/9/2016 2:02:55 PM]

select JSON_QUERY(s.FEATURE, '$.geometry') SOURCE_LOCATION, JSON_QUERY(t.FEATURE, '$.geometry') TARGET_LOCATION, JSON_QUERY(t.FEATURE, '$.properties') TARGET_PROPERTIES, SDO_GEOM.SDO_DISTANCE ( JSON_VALUE(t.FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR), JSON_VALUE(s.FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR), .05 ) DISTANCE from CITY_LOT_FEATURES s, CITY_LOT_FEATURES t where JSON_EXISTS( s.FEATURE, '$?(@.properties.MAPBLKLOT == $LOT)' passing '0001001' as "LOT" ) and SDO_WITHIN_DISTANCE( JSON_VALUE(t.FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR), JSON_VALUE(s.FEATURE, '$.geometry' returning SDO_GEOMETRY ERROR ON ERROR), 'distance=50' ) = 'TRUE' and NOT JSON_EXISTS( t.FEATURE, '$?(@.properties.MAPBLKLOT == $LOT)' passing '0001001' as "LOT" ) /