You are on page 1of 7

BROUGHT TO YOU IN PARTNERSHIP WITH

CONTENTS

Hybrid Relational/
∙ Hybrid Relational/JSON Overview

∙ Data Modeling and Querying

∙ Inserting Sample Data

∙ Reading Fields

JSON Data Modeling ∙ Creating and Updating Fields

∙ Searching Fields

∙ Reading Arrays

and Querying ∙ Creating and Updating Arrays

∙ Reading and Creating Objects

∙ Indexing Fields

∙ Integrity Constraints
SHANE JOHNSON
∙ Additional Resources
SR. DIRECTOR OF PRODUCT MARKETING AT MARIADB

JSON has become the de facto standard for sending and receiving have authors, movies have resolutions. Thus, these properties are
data — in particular, between application servers and the browsers/ stored in a separate JSON column. A JSON column also allows easy
mobile applications connected to them. addition of new properties in the future and properties with multiple
and/or complex values.
In the late 2000s, document databases such as MongoDB were built
to store and query JSON documents. Today, relational databases For example, a movie may have multiple cuts, with each cut having
can store and query data as relations (i.e., rows and columns) or its own title and running time. Because cuts are unique to a movie, it
JSON — and with standard SQL. may not make sense to store them in a separate table with a one-to-
many relationship.
There are a few ways to take advantage of the JSON support within
relational databases: DATA QUERYING
JSON documents are queried with SQL functions. The core functions
• Storing data as relations, querying it as JSON documents.
(e.g., reading a field within a JSON document) are defined in the
• Storing data as JSON documents, querying it as relations.
SQL:2016 standard.
• Storing data as relations with JSON documents, querying it
With these functions, queries can access both relations and JSON
as relations and/or JSON documents.
documents at the same time.
The third approach, hybrid relational/JSON data modeling and
querying, is covered in this DZone Refcard.

HYBRID RELATIONAL/JSON
OVERVIEW
Extending relations with JSON documents allows easier, faster
application development while at the same time maintaining the
operational benefits of relational databases — transactions, data
integrity, and relationships.

DATA MODELING
Attributes common to all objects should be stored as relations. The
examples in this DZone Refcard use a simple product catalog table for
both movies and books. Every product has a name and price, so the
product table has name and price columns. However, whereas books

1
THE SKY IS
TRULY
THE LIMIT
SKYSQL. The ultimate MariaDB Cloud is here.

EASY TO USE + POWERFUL


SkySQL is the first database-as-a-service (DBaaS) to unlock the full power of MariaDB Platform
in the cloud. Engineered for mission-critical applications, SkySQL does away with common
limitations and combines automation with the expertise and experience necessary to support
production deployments in the cloud.

Try SkySQL Now


HYBRID RELATIONAL/JSON DATA MODELING AND QUERYING

JSON functions often take one or more the following arguments: FOUNDATION "PROPS" (JSON DOCUMENT)

• The name of the column containing the JSON document. {


"author": "Isaac Asimov",
• One or more JSONPath expressions to fields within the
"page_count": 296
JSON document.
}

• A value (if searching fields or inserting/updating a field).

READING FIELDS
MARIADB SERVER
The JSON_VALUE function returns the value of string, number and
The examples below use the SQL JSON functions available in
boolean fields (i.e., scalar fields).
MariaDB Server 10.4. MariaDB Server implements all JSON functions
defined in the SQL:2016 standard and includes additional ones that Note: If the JSON document does not contain the field specified by the
may or may not be implemented in other relational databases. JSONPath expression, a NULL value will be returned.

INSERTING SAMPLE DATA JSON_VALUE

CREATE OR REPLACE TABLE products ( SELECT name, type, price,


id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, JSON_VALUE(props, '$.video.format') AS format
name VARCHAR(40) NOT NULL, FROM products
type VARCHAR(10) NOT NULL, WHERE id = 1;
price FLOAT(5, 2) NOT NULL,
props JSON NOT NULL);
NAME TYPE PRICE VIDEO

INSERT INTO products (name, type, price, props) Alien Blu-ray 14.99 Blu-ray
VALUES ('Alien', 'Movie', 14.99, '{"video": {"format":
"Blu-ray", "resolution": "1080p", "aspectRatio":
CREATING AND UPDATING FIELDS
"1.85:1"}, "cuts": [{"name": "Theatrical", "runtime":
138}, {"name": "Special Edition", "runtime": 155}], The JSON_INSERT, JSON_REPLACE , JSON_SET, and JSON_REMOVE
"audio": ["DTS HD", "Dolby Surround"]}'); functions modify JSON documents.

INSERT INTO products (name, type, price, props) Note: JSON_SET can insert a field or update an existing one.
VALUES ('Foundation', 'Book', 7.99, '{"author": "Isaac
Asimov", "page_count": 296}'); JSON_INSERT|REPLACE|SET

UPDATE products
SELECT * FROM products;
SET props = JSON_INSERT(props, '$.disks', 1)
WHERE id = 1;
ID NAME TYPE PRICE PROPS

1 Alien Movie 14.99 {...} UPDATE products


SET props = JSON_REPLACE(props, '$.disks', 1)
2 Foundation Book 7.99 {...}
WHERE id = 1;

ALIEN "PROPS" (JSON DOCUMENT)


UPDATE products
SET props = JSON_SET(props, "$.video.3d", true)
{
WHERE id = 1;
"video": {
"format": "Blu-ray",
SELECT name, type,
"resolution": "1080p",
JSON_VALUE(props, '$.disks') AS disks,
"aspectRatio": "1.85:1"
JSON_EXTRACT(props, '$.video.3d') AS "3d"
},
FROM products WHERE id = 1;
"audio": ["DTS HD", "Dolby Surround"],
"cuts": [{
Note: JSON_EXTRACT is used to return the value of the 3d field instead
"name": "Theatrical",
"runtime": 138 of JSON_VALUE so it is returned as a boolean (true|false) rather than a
}, { tinyint (0|1).
"name": "Special Edition",
"runtime": 155 NAME TYPE DISKS 3D
}]
Alien Movie 1 true
}

3 BROUGHT TO YOU IN PARTNERSHIP WITH


HYBRID RELATIONAL/JSON DATA MODELING AND QUERYING

SEARCHING FIELDS NAME TYPE AUDIO


The JSON_CONTAINS, JSON_CONTAINS_PATH, and JSON_EXISTS
Alien Movie DTS HD
functions return whether or not JSON documents contain specific
fields or fields with specific values.
CREATING AND UPDATING ARRAYS
JSON_CONTAINS The JSON_ARRAY function creates an array while the JSON_ARRAY_

The JSON_CONTAINS function returns 1 if the JSON document APPEND and JSON_ARRAY_INSERT functions add an element to an

contains a field with the value provided, optionally at a provided array, the former at the end and the latter at a specific position.

path. Otherwise, it returns 0.


Note: JSON_REMOVE can be used to remove an element from an array
SELECT id, name, price by providing a JSONPath expression with an index position.
FROM products
WHERE type = 'Movie' AND JSON_ARRAY
JSON_CONTAINS(props, '\"DTS HD\"', '$.audio') = 1;
UPDATE products
SET props = JSON_INSERT(props, '$.languages',
ID NAME PRICE JSON_ARRAY('English', 'German'))
1 Alien 14.99 WHERE id = 1;

SELECT name, type,


JSON_EXISTS
JSON_QUERY(props, '$.languages') AS languages
The JSON_EXISTS function returns 1 if the JSON document contains FROM products
WHERE id = 1;
a field at the provided path. Otherwise, it returns 0.

SELECT id, name, price


NAME TYPE LANGUAGES
FROM products
Alien Movie ["English", "German"]
WHERE type = 'Movie' AND
JSON_EXISTS(props, '$.video.format') = 1;
JSON_ARRAY_APPEND|INSERT
ID NAME PRICE
UPDATE products
1 Alien 14.99 SET props =
JSON_ARRAY_APPEND(props, '$.languages', 'French')
WHERE id = 1;
READING ARRAYS
UPDATE products
JSON_QUERY
SET props =
The JSON_QUERY function returns the value of array and object JSON_ARRAY_INSERT(props, '$.languages[1]',
fields (i.e., compound fields). 'Spanish')
WHERE id = 1;
SELECT name, type,
JSON_QUERY(props, '$.audio') AS audio SELECT name, type,
FROM products JSON_QUERY(props, '$.languages') AS languages
WHERE id = 1; FROM products
WHERE id = 1;

NAME TYPE AUDIO


Alien Movie ["DTS HD", "Dolby Surround"] NAME TYPE LANGUAGES
["English", "Spanish", "German",
The JSON_QUERY and JSON_VALUE functions can be used to return Alien Movie
"French"]
the value of an element within an array.

SELECT name, type,


JSON_VALUE(props, '$.audio[0]') AS audio
FROM products
WHERE id = 1;

4 BROUGHT TO YOU IN PARTNERSHIP WITH


HYBRID RELATIONAL/JSON DATA MODELING AND QUERYING

READING OBJECTS ALTER TABLE products


ADD COLUMN video_resolution VARCHAR(5) AS
The JSON_QUERY function returns the value of array and object fields
(JSON_VALUE(props, '$.video.resolution')) VIRTUAL;
(i.e., compound fields).

EXPLAIN
JSON_QUERY
SELECT name, type, price
FROM products
SELECT name, JSON_QUERY(props, '$.video') AS video
WHERE video_resolution = '1080p';
FROM products
WHERE id = 1;
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS
NAME AUDIO 1 simple products all null
{"format": "Blu-ray", "resolution": "1080p",
Alien
"aspectRatio": "1.85:1", "3d": true} CREATE INDEX resolutions ON products(video_resolution);

EXPLAIN
CREATING OBJECTS
SELECT name, type, price
JSON_OBJECT FROM products
WHERE video_resolution = '1080p';
The JSON_OBJECT function creates a JSON document from key/
value pairs. The key is arbitrary. The value can be the name of a non-
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS
JSON column or the scalar result of a JSON function.
1 simple products ref resolutions
SELECT id, JSON_OBJECT('name', name,
'type', type, 'price', price) AS json
FROM products INTEGRITY CONSTRAINTS
WHERE id = 1; JSON functions can be used in check constraints in order to enforce
the integrity of JSON documents — for example, to ensure required
ID JSON fields, field types, and field sizes.
1 {"name": "Alien", "type": "Movie", "price": 14.99}
ALTER TABLE products
ADD CONSTRAINT check_attr
JSON_MERGE_PATCH CHECK (type != ‘M’ or (type = ‘M’ and
JSON_TYPE(JSON_QUERY(attr, ‘$.video’)) = ‘OBJECT’
The JSON_MERGE_PATCH function merges two JSON documents,
and
and can be used with JSON_OBJECT to create a JSON document by JSON_TYPE(JSON_QUERY(attr, ‘$.cuts’)) = ‘ARRAY’ and
merging one or more non-JSON columns with a JSON column. JSON_TYPE(JSON_QUERY(attr, ‘$.audio’)) = ‘ARRAY’
and
SELECT JSON_MERGE( JSON_TYPE(JSON_VALUE(attr, ‘$.disks’)) = ‘INTEGER’
JSON_OBJECT('id', id, 'name', name), props) AS and
data JSON_EXISTS(attr, ‘$.video.resolution’) = 1 and
FROM products JSON_EXISTS(attr, ‘$.video.aspectRatio’) = 1 and
WHERE id = 1; JSON_LENGTH(JSON_QUERY(attr, ‘$.cuts’)) > 0 and
JSON_LENGTH(JSON_QUERY(attr, ‘$.audio’)) > 0));

JSON
INSERT INTO products (type, name, format, price, attr)
{"id": 1, "name": "Alien", "video": {"format": "Blu-
VALUES (‘M’, ‘Tron’, ‘Blu-ray’, 29.99, ‘{“video”:
ray", "resolution": "1080p", "aspectRatio": "1.85:1",
{“aspectRatio”: “2.21:1”}, “cuts”: [{“name”:
"3d": true}, "cuts": [{"name": "Theatrical", "runtime":
“Theatrical”, “runtime”:96}], “audio”: [“DTS HD”,
138}, {"name": "Special Edition", "runtime": 155}],
“Dolby Digital”], “disks”: 1}’);
"audio": ["DTS HD", "Dolby Surround"], "disks": 1,
"languages": ["English", "Spanish", "German", "French"]}
Note: The statement above is inserting a movie without a
resolution field.
INDEXING FIELDS
ERROR 4025 (23000): CONSTRAINT ‘check_attr’ failed for
Indexes can be created on fields within JSON documents by creating
‘test’.’products’
virtual columns (i.e., non-persistent, computed columns) using a
JSON function and indexing them.

5 BROUGHT TO YOU IN PARTNERSHIP WITH


HYBRID RELATIONAL/JSON DATA MODELING AND QUERYING

MARIADB SERVER SQL JSON FUNCTIONS


NAME TYPE SQL:2016

JSON_ARRAY Create a JSON array from one or more key/value pairs Y

JSON_ARRAYAGG Y

JSON_ARRAY_APPEND Append an element to a JSON array N

JSON_ARRAY_INSERT Insert an element into a JSON array N

JSON_COMPACT Remove unnecessary white space from a JSON document N

JSON_CONTAINS See if a JSON document or field contains a specific value N

JSON_CONTAINS_PATH See if a JSON document contains one or more specific fields N

JSON_DEPTH Get the maximum depth of a JSON document N

JSON_DETAILED Format a JSON document so it is easy to read N

JSON_EXISTS See if a JSON document contains a specific field Y

JSON_EXTRACT Get one or more specific fields from a JSON document N

JSON_INSERT Insert a field into a JSON document N

JSON_KEYS Get all of the keys within a JSON document or a nested JSON object N

JSON_LENGTH Get the length of a JSON document or the value of a specific field N

JSON_MERGE_PATCH Merge two or more JSON documents N

JSON_OBJECT Create a JSON object from one or more key/value pairs Y

JSON_OBJECTAGG Y

JSON_QUERY Get the value of a specific JSON object or array within a JSON document Y

JSON_QUOTE Create a JSON string from a string N

JSON_REMOVE Remove a field from a JSON document N

JSON_REPLACE Replace the value of a specific field within a JSON document N

JSON_SEARCH Get the first or all paths to fields containing a specific value N

JSON_SET Insert or update the value of a specific field within a JSON document N

JSON_TABLE* Get a JSON document as a table with specific columns Y

JSON_TYPE Get the type of a specific field within a JSON document N

JSON_UNQUOTE Create a string from a JSON string N

JSON_VALID See if the JSON document is valid JSON N

JSON_VALUE Get value of a specific JSON string, number of boolean within a JSON document Y

*The JSON_TABLE function is planned for MariaDB Server 10.5 (beta as of March 2020).

6 BROUGHT TO YOU IN PARTNERSHIP WITH


HYBRID RELATIONAL/JSON DATA MODELING AND QUERYING

CONCLUSION GETTING STARTED


Today, JSON is supported within relational databases and via The best way to get started with hybrid relational/JSON data
standard SQL. With existing databases, even if they are fully modeling and querying is to download and install MariaDB Server.
normalized, SQL queries can be written (or rewritten) to return
Once a MariaDB Server database is up and running, copy and paste
JSON documents instead of rows and columns, simplifying the
the SQL queries located on page 6 of this DZone Refcard — including
development of web and mobile applications that use JSON to
the insert statements — to see the JSON support in action firsthand.
send and receive data.
Documentation for all the JSON SQL functions can be found in the
To make it even easier, SQL JSON functions can be used to MariaDB Knowledge Base.
create views that expose rows and columns as JSON documents,
allowing developers to query relational data as though it were
stored as JSON documents instead. Written by Shane Johnson,
Sr. Director of Product Marketing at MariaDB

For new applications, or applications being refactored, now is Shane Johnson is the Senior Director of Product
the perfect time to adopt a hybrid relational/JSON data model. Marketing at MariaDB. Prior to MariaDB, he led
product and technical marketing at Couchbase.
Identifying places where the use of JSON documents — both in the In a past life, he performed technical roles in development,
data model and in query results — can streamline development architecture, and evangelism at Red Hat and other companies.
His background is in Java and distributed systems.
and provide greater flexibility in the future.

DZone, a Devada Media Property, is the resource software Devada, Inc.


developers, engineers, and architects turn to time and again 600 Park Offices Drive
to learn new skills, solve software development problems, Suite 150
and share their expertise. Every day, hundreds of tousands Research Triangle Park, NC 27709
of developers come to DZone to read about the latest
888.678.0399 919.678.0300
technologies, methodologies, and best practices. That makes
DZone the ideal place for developer marketers to build product Copyright © 2020 Devada, Inc. All rights reserved. No
and brand awareness and drive sales. DZone clients include part of this publication may be reporoduced, stored in a
some of the most innovative technology and tech-enabled retrieval system, or transmitted, in any form or by means
companies in the world including Red Hat, Cloud Elements, of electronic, mechanical, photocopying, or otherwise,
Sensu, and Sauce Labs. without prior written permission of the publisher.

7 BROUGHT TO YOU IN PARTNERSHIP WITH

You might also like