Summary: in this tutorial, you will learn how to create an index for JSON data in MySQL using functional indexes.
Setting up a sample table
We’ll use the products
table from the sample database as a data source for creating JSON data:
First, create a new table called product_info
:
CREATE TABLE product_results(
id INT AUTO_INCREMENT PRIMARY KEY,
data JSON
);
Code language: SQL (Structured Query Language) (sql)
The product_results
table has two columns:
id
: This is an auto-increment primary key column.data
: This column has the data type of JSON that stores the JSON data.
Next, create JSON data from the columns of the products
table and insert it into the product_results
table:
INSERT INTO product_results(data)
SELECT
JSON_OBJECT(
'buyPrice', buyPrice, 'MSRP', msrp,
'quantityInStock', quantityInStock,
'productName', productName
)
FROM
products;
Code language: SQL (Structured Query Language) (sql)
The statement inserts 110 rows into the product_results
table.
Then, query data from the product_results
table:
SELECT * FROM product_results;
Code language: SQL (Structured Query Language) (sql)
Output:
+-----+--------------------------------------------------------------------------------------------------------------------------+
| id | data |
+-----+--------------------------------------------------------------------------------------------------------------------------+
| 1 | {"MSRP": 95.70, "buyPrice": 48.81, "productName": "1969 Harley Davidson Ultimate Chopper", "quantityInStock": 7933} |
| 2 | {"MSRP": 214.30, "buyPrice": 98.58, "productName": "1952 Alpine Renault 1300", "quantityInStock": 7305} |
| 3 | {"MSRP": 118.94, "buyPrice": 68.99, "productName": "1996 Moto Guzzi 1100i", "quantityInStock": 6625} |
...
Code language: SQL (Structured Query Language) (sql)
After that, find the product with the name 'P-51-D Mustang'
:
SELECT
data ->> '$.productName',
data ->> '$.buyPrice'
FROM
product_results
WHERE
data ->> '$.productName' = 'P-51-D Mustang';
Code language: SQL (Structured Query Language) (sql)
It returns the following product:
+--------------------------+-----------------------+
| data ->> '$.productName' | data ->> '$.buyPrice' |
+--------------------------+-----------------------+
| P-51-D Mustang | 49.00 |
+--------------------------+-----------------------+
1 row in set (0.00 sec)
Code language: SQL (Structured Query Language) (sql)
The query works as expected but it has to scan all the rows in the product_results
table to find the product.
Finally, show how the query optimizer executes the query by using the EXPLAIN
statement:
EXPLAIN SELECT
data ->> '$.productName',
data ->> '$.buyPrice'
FROM
product_results
WHERE
data ->> '$.productName' = 'P-51-D Mustang' \G;
Code language: SQL (Structured Query Language) (sql)
Output:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: product_results
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 110
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
Code language: SQL (Structured Query Language) (sql)
To speed up the query, you need to index the JSON in the data column. However, MySQL does not support indexing JSON data directly.
Fortunately, you can use functional indexes, which have been available since MySQL 8.0.13, to index JSON data.
Indexing JSON data using functional indexes
A functional index is an index based on an expression rather than a column. The following statement adds an index to the product_results
table:
ALTER TABLE
product_results
ADD
INDEX idx_product_name(
(data ->> '$.productName')
);
Code language: SQL (Structured Query Language) (sql)
But it returns an error:
ERROR 3757 (HY000): Cannot create a functional index on an expression that returns a BLOB or TEXT. Please consider using CAST.
Code language: SQL (Structured Query Language) (sql)
The reason is that the ->> operator returns a value of the TEXT
type that is not indexable.
The error message also shows that we need to use the CAST
operator to cast the value to an indexable type e.g., VARCHAR
in this case:
ALTER TABLE
product_results
ADD
INDEX idx_product_name (
(
CAST(
data ->> '$.productName' AS CHAR(255)
) COLLATE utf8mb4_bin
)
);
Code language: SQL (Structured Query Language) (sql)
The index is created successfully.
Note that casting using CHAR instructs MySQL to infer a VARCHAR type.
Now, you can query the products by the buy price from the JSON data:
SELECT
data ->> '$.productName',
data ->> '$.buyPrice'
FROM
product_results
WHERE
data ->> '$.productName' = 'P-51-D Mustang';
Code language: SQL (Structured Query Language) (sql)
It returns the same result set as before. But this time, it uses the functional index idx_product_name
:
EXPLAIN SELECT
data ->> '$.productName',
data ->> '$.buyPrice'
FROM
product_results
WHERE
data ->> '$.productName' = 'P-51-D Mustang' \G;
Code language: SQL (Structured Query Language) (sql)
Output:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: product_results
partitions: NULL
type: ref
possible_keys: idx_product_name
key: idx_product_name
key_len: 1023
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
Code language: SQL (Structured Query Language) (sql)
Summary
- Use functional indexes to index JSON data to improve the query speed.