44Working with the JSON Data Type
55*******************************
66
7- Native support for JSON data was introduced in Oracle database 12c. You can use
8- the relational database to store and query JSON data and benefit from the easy
9- extensibility of JSON data while retaining the performance and structure of the
10- relational database. JSON data is stored in the database in BLOB, CLOB or
11- VARCHAR2 columns. For performance reasons, it is always a good idea to store
12- JSON data in BLOB columns. To ensure that only JSON data is stored in that
13- column, use a check constraint with the clause ``is JSON `` as shown in the
14- following SQL to create a table containing JSON data:
7+ Native support for JSON data was introduced in Oracle Database 12c. You can
8+ use JSON with relational database features, including transactions, indexing,
9+ declarative querying, and views. You can project JSON data relationally,
10+ making it available for relational processes and tools. Also see
11+ :ref: `Simple Oracle Document Access (SODA) <sodausermanual >`, which allows
12+ access to JSON documents through a set of NoSQL-style APIs.
13+
14+ Prior to Oracle Database 21, JSON in relational tables is stored as BLOB, CLOB
15+ or VARCHAR2 data, allowing easy access with cx_Oracle. Oracle Database 21
16+ introduced a dedicated JSON data type with a new `binary storage format
17+ <https://blogs.oracle.com/jsondb/osonformat> `__ that improves performance and
18+ functionality. To use the new dedicated JSON type, the Oracle Database and
19+ Oracle Client libraries must be version 21, or later. Also cx_Oracle must be
20+ 8.1, or later.
21+
22+ For more information about using JSON in Oracle Database see the
23+ `Database JSON Developer's Guide
24+ <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN> `__.
25+
26+ In Oracle Database 21, to create a table with a column called ``JSON_DATA `` for
27+ JSON data:
28+
29+ .. code-block :: sql
30+
31+ create table customers (
32+ id integer not null primary key,
33+ json_data json
34+ );
35+
36+ For older Oracle Database versions the syntax is:
1537
1638.. code-block :: sql
1739
@@ -20,57 +42,269 @@ following SQL to create a table containing JSON data:
2042 json_data blob check (json_data is json)
2143 );
2244
23- The following Python code can then be used to insert some data into the
24- database:
45+ The check constraint with the clause ``IS JSON `` ensures only JSON data is
46+ stored in that column.
47+
48+ The older syntax can still be used in Oracle Database 21, however the
49+ recommendation is to move to the new JSON type. With the old syntax, the
50+ storage can be BLOB, CLOB or VARCHAR2. Of these, BLOB is preferred to avoid
51+ character set conversion overheads.
52+
53+ Using Oracle Database 21 and Oracle Client 21 with cx_Oracle 8.1 (or later),
54+ you can insert by binding as shown below:
2555
2656.. code-block :: python
2757
28- import json
58+ import datetime
2959
30- customerData = dict (name = " Rod" , dept = " Sales" , location = " Germany" )
31- cursor.execute(" insert into customers (id, json_data) values (:1, :2)" ,
32- [1 , json.dumps(customerData)])
60+ json_data = [
61+ 2.78 ,
62+ True ,
63+ ' Ocean Beach' ,
64+ b ' Some bytes' ,
65+ {' keyA' : 1 , ' KeyB' : ' Melbourne' },
66+ datetime.date.today()
67+ ]
68+
69+ var = cursor.var(cx_Oracle.DB_TYPE_JSON )
70+ var.setvalue(0 , json_data)
71+ cursor.execute(" insert into customers values (:1, :2)" , [123 , var])
3372
34- The data can be retrieved in its entirety using the following code:
73+ # or these two lines can replace the three previous lines
74+ cursor.setinputsizes(None , cx_Oracle.DB_TYPE_JSON )
75+ cursor.execute(" insert into customers values (:1, :2)" , [123 , json_data])
76+
77+ Fetching with:
3578
3679.. code-block :: python
3780
38- import json
81+ for row in cursor.execute(" SELECT c.json_data FROM customers c" ):
82+ print (row)
3983
40- for blob, in cursor.execute(" select json_data from customers" ):
41- data = json.loads(blob.read())
42- print (data[" name" ]) # will print Rod
84+ gives output like::
85+
86+ ([Decimal('2.78'), True, 'Ocean Beach',
87+ b'Some bytes',
88+ {'keyA': Decimal('1'), 'KeyB': 'Melbourne'},
89+ datetime.datetime(2020, 12, 2, 0, 0)],)
4390
44- If only the department needs to be read, the following code can be used
45- instead:
91+ With the older BLOB storage, or to insert JSON strings, use:
4692
4793.. code-block :: python
4894
49- for deptName, in cursor.execute(" select c.json_data.dept from customers c" ):
50- print (deptName) # will print Sales
95+ import json
96+
97+ customer_data = dict (name = " Rod" , dept = " Sales" , location = " Germany" )
98+ cursor.execute(" insert into customers (id, json_data) values (:1, :2)" ,
99+ [1 , json.dumps(customer_data)])
100+
101+
102+ IN Bind Type Mapping
103+ ====================
104+
105+ When binding to a JSON value, the type parameter for the variable must be
106+ specified as :data: `cx_Oracle.DB_TYPE_JSON `. Python values are converted to
107+ JSON values as shown in the following table. The 'SQL Equivalent' syntax can
108+ be used in SQL INSERT and UPDATE statements if specific attribute types are
109+ needed but there is no direct mapping from Python.
110+
111+ .. list-table ::
112+ :header-rows: 1
113+ :widths: 1 1 1
114+ :align: left
51115
52- You can convert the data stored in relational tables into JSON data by using
53- the JSON_OBJECT SQL operator. For example:
116+ * - Python Type or Value
117+ - JSON Attribute Type or Value
118+ - SQL Equivalent Example
119+ * - None
120+ - null
121+ - NULL
122+ * - True
123+ - true
124+ - n/a
125+ * - False
126+ - false
127+ - n/a
128+ * - int
129+ - NUMBER
130+ - json_scalar(1)
131+ * - float
132+ - NUMBER
133+ - json_scalar(1)
134+ * - decimal.Decimal
135+ - NUMBER
136+ - json_scalar(1)
137+ * - str
138+ - VARCHAR2
139+ - json_scalar('String')
140+ * - datetime.date
141+ - TIMESTAMP
142+ - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
143+ * - datetime.datetime
144+ - TIMESTAMP
145+ - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
146+ * - bytes
147+ - RAW
148+ - json_scalar(utl_raw.cast_to_raw('A raw value'))
149+ * - list
150+ - Array
151+ - json_array(1, 2, 3 returning json)
152+ * - dict
153+ - Object
154+ - json_object(key 'Fred' value json_scalar(5), key 'George' value json_scalar('A string') returning json)
155+ * - n/a
156+ - CLOB
157+ - json_scalar(to_clob('A short CLOB'))
158+ * - n/a
159+ - BLOB
160+ - json_scalar(to_blob(utl_raw.cast_to_raw('A short BLOB')))
161+ * - n/a
162+ - DATE
163+ - json_scalar(to_date('2020-03-10', 'YYYY-MM-DD'))
164+ * - n/a
165+ - INTERVAL YEAR TO MONTH
166+ - json_scalar(to_yminterval('+5-9'))
167+ * - n/a
168+ - INTERVAL DAY TO SECOND
169+ - json_scalar(to_dsinterval('P25DT8H25M'))
170+ * - n/a
171+ - BINARY_DOUBLE
172+ - json_scalar(to_binary_double(25))
173+ * - n/a
174+ - BINARY_FLOAT
175+ - json_scalar(to_binary_float(15.5))
176+
177+ An example of creating a CLOB attribute with key ``mydocument `` in a JSON column
178+ using SQL is:
54179
55180.. code-block :: python
56181
57- import json
58182 cursor.execute("""
59- select json_object(
60- 'id' value employee_id,
61- 'name' value (first_name || ' ' || last_name))
62- from employees where rownum <= 3""" )
63- for value, in cursor:
64- print (json.loads(value,))
183+ insert into mytab (myjsoncol) values
184+ (json_object(key 'mydocument' value json_scalar(to_clob(:b))
185+ returning json))""" ,
186+ [' A short CLOB' ])
187+
188+ When `mytab ` is queried in cx_Oracle, the CLOB data will be returned as a
189+ Python string, as shown by the following table. Output might be like::
65190
66- The result is::
191+ {mydocument: 'A short CLOB'}
67192
68- {'id': 100, 'name': 'Steven King'}
69- {'id': 101, 'name': 'Neena Kochhar'}
70- {'id': 102, 'name': 'Lex De Haan'}
71193
194+ Query and OUT Bind Type Mapping
195+ ===============================
72196
73- See `JSON Developer's Guide
197+ When getting Oracle Database 21 JSON values from the database, the following
198+ attribute mapping occurs:
199+
200+ .. list-table ::
201+ :header-rows: 1
202+ :widths: 1 1
203+ :align: left
204+
205+ * - Database JSON Attribute Type or Value
206+ - Python Type or Value
207+ * - null
208+ - None
209+ * - false
210+ - False
211+ * - true
212+ - True
213+ * - NUMBER
214+ - decimal.Decimal
215+ * - VARCHAR2
216+ - str
217+ * - RAW
218+ - bytes
219+ * - CLOB
220+ - str
221+ * - BLOB
222+ - bytes
223+ * - DATE
224+ - datetime.datetime
225+ * - TIMESTAMP
226+ - datetime.datetime
227+ * - INTERVAL YEAR TO MONTH
228+ - not supported
229+ * - INTERVAL DAY TO SECOND
230+ - datetime.timedelta
231+ * - BINARY_DOUBLE
232+ - float
233+ * - BINARY_FLOAT
234+ - float
235+ * - Arrays
236+ - list
237+ * - Objects
238+ - dict
239+
240+ SQL/JSON Path Expressions
241+ =========================
242+
243+ Oracle Database provides SQL access to JSON data using SQL/JSON path
244+ expressions. A path expression selects zero or more JSON values that match, or
245+ satisfy, it. Path expressions can use wildcards and array ranges. A simple
246+ path expression is ``$.friends `` which is the value of the JSON field
247+ ``friends ``.
248+
249+ For example, the previously created ``customers `` table with JSON column
250+ ``json_data `` can be queried like:
251+
252+ .. code-block :: sql
253+
254+ select c.json_data.location FROM customers c
255+
256+ With the JSON ``'{"name":"Rod","dept":"Sales","location":"Germany"}' `` stored
257+ in the table, the queried value would be ``Germany ``.
258+
259+ The JSON_EXISTS functions tests for the existence of a particular value within
260+ some JSON data. To look for JSON entries that have a ``location `` field:
261+
262+ .. code-block :: python
263+
264+ for blob, in cursor.execute("""
265+ select json_data
266+ from customers
267+ where json_exists(json_data, '$.location')""" ):
268+ data = json.loads(blob.read())
269+ print (data)
270+
271+ This query might display::
272+
273+ {'name': 'Rod', 'dept': 'Sales', 'location': 'Germany'}
274+
275+ The SQL/JSON functions ``JSON_VALUE `` and ``JSON_QUERY `` can also be used.
276+
277+ Note that the default error-handling behavior for these functions is
278+ ``NULL ON ERROR ``, which means that no value is returned if an error occurs.
279+ To ensure that an error is raised, use ``ERROR ON ERROR ``.
280+
281+ For more information, see `SQL/JSON Path Expressions
74282<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
75- id=GUID-17642E43-7D87-4590-8870-06E9FDE9A6E9> `__ for more information about
76- using JSON in Oracle Database.
283+ id=GUID-2DC05D71-3D62-4A14-855F-76E054032494> `__
284+ in the Oracle JSON Developer's Guide.
285+
286+
287+ Accessing Relational Data as JSON
288+ =================================
289+
290+ In Oracle Database 12.2, or later, the `JSON_OBJECT
291+ <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1EF347AE-7FDA-4B41-AFE0-DD5A49E8B370> `__
292+ function is a great way to convert relational table data to JSON:
293+
294+ .. code-block :: python
295+
296+ cursor.execute("""
297+ select json_object('deptId' is d.department_id, 'name' is d.department_name) department
298+ from departments d
299+ where department_id < :did
300+ order by d.department_id""" ,
301+ [50 ]);
302+ for row in cursor:
303+ print (row)
304+
305+ This produces::
306+
307+ ('{"deptId":10,"name":"Administration"}',)
308+ ('{"deptId":20,"name":"Marketing"}',)
309+ ('{"deptId":30,"name":"Purchasing"}',)
310+ ('{"deptId":40,"name":"Human Resources"}',)
0 commit comments