Using an SQL interface to handle JSON data in Db2 for z/OS
This tutorial focuses on an SQL interface in Db2 for z/OS® that enables you to extract and retrieve JSON data from BSON objects and to convert from JSON to BSON. By using this interface, you can manage JSON data without relying on Db2 NoSQL JSON APIs. Instead, SQL interfaces can be used for JSON manipulation. Setup and configuration guidance is provided, as well as many examples for common JSON usage inside Db2 for z/OS. Hints and tips are provided to improve performance and prevent potential pitfalls.
Introduction
Db2 for z/OS supports document storage and manipulation for JSON documents. For documentation manipulation, it relies on Db2 NoSQL JSON APIs. JSON data is stored internally as BSON in an inline BLOB column. The conversion between JSON and BSON is handled by the Db2 NoSQL JSON APIs.
>>-JSON_VAL—(--json-value--,--search-string--,--result-type--)---------><
The JSON_VAL function returns an element of a JSON document that is identified by the JSON field name specified in search-string
. The value of the JSON element is returned in the data type and length that is specified in result-type
. For more information, see JSON_VAL.
These two UDFs, together with JSON_VAL, enable you to use SQL to do basic manipulation of JSON data. The invocation of these Db2 functions is referred to as SQL interfaces (or APIs) in this tutorial.
What is JSON?
JavaScript Object Notation (JSON) is a lightweight data format specified in IETF RFC 4627 and is based on a subset of the JavaScript programming language. JSON is a text format that is easy for humans to read and write, and easy for machines to parse and generate. With the increasing popularity of JavaScript and the simplicity of JSON itself, JSON has become popular for presenting information to JavaScript clients.
The following figure shows a simple example of a JSON document that illustrates a purchase order.
Setup and configuration
To enable JSON support in Db2 12 for z/OS you need the server-side UDFs for JSON document access, which are available in Db2 Accessories Suite for z/OS version 4.1 (FMID H2AS410).
To create the server-side UDFs, run the DDL in the Db2 Accessories Suite. A WLM environment must be configured for these UDFs.
Most of these UDFs are helpers that we don't normally use directly in an SQL statement, except SYSTOOLS.JSON2BSON and SYSTOOLS.BSON2JSON. The following two CREATE FUNCTION statements illustrate the definition of these UDFs.
Note that JSON2BSON() and BSON2JSON() cannot apply on VARCHAR column.
The following sections show how to create a table to store, insert, select, and update JSON data.
Creating a table to store JSON documents
To use the SQL APIs to manipulate JSON documents, the data type of the column that is used to store JSON documents must be declared as an inline BLOB. The first two SQL statements in Figure 4 show how to create table to store JSON data. Although these tables use the same definition as the table implicitly created by JSON API, it is not required if you use only SQL APIs. By taking this approach, you have more flexibility in controlling the database objects to store and manipulate JSON data than the objects implicitly created by the JSON API. For example, you can create the table in a particular table space or database, and use a particular bufferpool; you can define an inline length other than 25000, BLOB column length other than 16M, etc.
The JSONPO table will store the purchase order in binary format of JSON (BSON) in an inline BLOB column. The JSONCUSTOMER table will store customer information in binary format of JSON(BSON) in an inline BLOB column. The JSONPOTEXT table will store JSON format of purchase order in a VARCHAR column.
Inserting JSON documents
Db2 stores JSON documents in BSON format, so we need a function to convert JSON text to BSON format to insert JSON documents into Db2 tables. SYSTOOLS.JSON2BSON serves this purpose: it receives JSON data in text format and returns BSON format to the caller. The following example INSERT statement shows how to insert JSON data into the table by invoking the SYSTOOLS.JSON2BSON function.
Selecting whole JSON documents
If you select the JSON document directly from the column without using any functions, you'll see a JSON document in not very readable BSON format (because Db2 stores JSON as BSON internally as shown in the following figure).
To see the JSON document in a readable format, you need to invoke the SYSTOOLS.BSON2JSON function. This function receives the JSON data in a BSON format and returns the text format to the caller. So, by passing the column with JSON document to SYSTOOLS.BSON2JSON function, you'll get readable JSON data back. The following example SELECT statement illustrates how to use BSON2JSON() to retrieve the JSON document in text format.
Creating JSON index
To improve performance during SELECT, you can create an index on
PO.customer.@cid
in JSON, as shown in the following figure. First, write an
expression using JSON_VAL to retrieve the PO.customer.@cid
. Its desired return type
is an integer, so i
is specified. Additionally, you need to append
:na
to ensure that array type is not returned because Db2 does not support an array type yet. A composite index can also be created. You can write multiple JSON_VAL expressions in a single index to improve performance on various fields in a JSON document.
Selecting part of a JSON document
To retrieve the value of a specific field in a JSON document, you need to invoke a JSON_VAL function, which is a built-in function that provides an SQL interface to extract and retrieve JSON data into SQL data types from BSON objects. Its schema is SYSIBM. This function accepts only the BSON type of JSON document, so its argument needs to be a column from the table that contains the JSON document in BSON format or a SYSTOOLS.JSON2BON function that returns the BSON format of JSON document.
The following example SELECT statement shows how to invoke JSON_VAL to retrieve the value of JSON
field in SQL data type. We're looking for the first productName
under
PO.items.item
for PO.customers.@cid=999
.
Expected output:
Lawnmower
- If you specify a path that does not exist, JSON_VAL returns a null value instead of an error message, as shown in Figure 11.
- JSON_VAL will also return a null value if JSON data cannot be converted to the specified data type due to type incompatibility. In Figure 12, we want to convert PO.items.item.0.productName into integer, and a null value will be returned.
- If
:na
is specified in the third argument of function invocation and an array is found, an error is returned. - If
:na
is not explicitly specified (by default), JSON_VAL will return the first element of the array, when an array is found.
Expected output:
<null>
<null>
2 record(s) selected
Expected output:
<null>
<null>
2 record(s) selected
The following table shows the supported result types of JSON_VAL:
Result type | Function return type/length | Notes |
---|---|---|
'n' | DECFLOAT(34) | |
'i' | INTEGER | |
'l' | BIGINT | |
'f' | DOUBLE | |
'd' | DATE | |
'ts' | TIMESTAMP | |
't' | TIME | |
's:n' | VARCHAR(n) | 'n' means return bytes of the result data. Returns the null value if the result data is longer than 'n'. 'n' is an integer value in the range 1 - 32672. |
'b:n' | VARCHAR(n) FOR BIT DATA | 'n' means return bytes of the result data. Returns the null value if the result data is longer than 'n'. 'n' is an integer value in the range 1 - 32672 |
'u' | INTEGER / 4 | Return 0 for elements whose values are explicitly set as NULL in the JSON document. If the value is set, but is NOT set to NULL, then return 1. For missing fields, this will return the null value. |
Selecting data from joined JSON tables
We can query JSON data from multiple tables in a single SQL statement. In the following example SELECT statement, we're looking for all the customer names (Customer.name) from the JSONCUSTOMER table in which PO.customer.@cid in the JSONPO table is the same as Customer.@cid in JSONCUSTOMER.
Expected output:
Customer Name
Michael
George
2 record(s) selected
Combining multiple JSON documents
You can combine two or more JSON documents into a single query by using the set operator. The following example SQL statement combines two JSON documents from different sources by using an UNION set operator. This example has a UNION operator with two legs: one leg contains JSON data from JSONPO table, and the other leg contains JSON data from the JSONPOTXT table. Because the JSONPOTXT table stores JSON in VARCHAR column, you need to use JSON2BSON to convert the textual format to binary format, then use BSON2JSON to convert to JSON format before UNION.
Note that JSON2BSON() is used here to verify that DATA from the JSONPOTXT table is in a valid JSON format.
Sorting JSON documents
You can sort on JSON documents by invoking the JSON_VAL function in the ORDER BY clause. The following example SELECT statement returns JSON documents ordered (descending) by the PO.customer.@cid field.
Updating JSON documents
You can update a JSON document in the table. The following example SQL statement updates a JSON
document by invoking two functions: SYSTOOLS.JSON2BSON and JSON_VAL. In this example, JSON_VAL is
invoked in the search condition to find the record to be updated ('Customer.@cid'
=888
), and SYSTOOLS.JSON2BSON is invoked to pass the new JSON data.
Deleting JSON documents
Similar to the UPDATE operation on JSON documents, you can delete a JSON document from the table.
The following example SQL statement deletes a JSON document. By invoking JSON_VAL in the search
condition of the DELETE statement, you can locate the record to be deleted ('Customer.@cid'
=777
), and the DELETE statement deletes the JSON data if it's found.