MySQL 8.3 introduced the explain_json_format_version system variable that allows us to specify which JSON output format should be used when we use the EXPLAIN statement with FORMAT=JSON.
It also applies when we omit the FORMAT argument and the explain_format system variable is set to JSON (because explain_format specifies the default format to be used by EXPLAIN).
The output of the EXPLAIN statement can be in one of three formats: TRADITIONAL (or its synonym DEFAULT), TREE, or JSON. The explain_json_format_version variable is only relevant when the output format is JSON.
Currently explain_json_format_version can be set to either 1 or 2.
Set the explain_json_format_version Value
We can set the explain_json_format_version variable like this:
SET explain_json_format_version = 1;
Result:
Query OK, 0 rows affected (0.01 sec)
That’s all that’s needed. The variable is now set to 1 (which happens to be the default value anyway).
Get the explain_json_format_version Value
We can get the current value of the explain_json_format_version system variable by selecting @@explain_json_format_version. Like this:
SELECT @@explain_json_format_version;
Result:
+-------------------------------+ | @@explain_json_format_version | +-------------------------------+ | 1 | +-------------------------------+
So we can see that it’s set to 1, which is the default value for this system variable.
Valid Values
As of this writing, we can set the explain_json_format_version variable to either 1 or 2.
As mentioned, 1 is the default value.
Here’s what the MySQL documentation says about the JSON format 2:
this JSON output format is based on access paths, and is intended to provide better compatibility with future versions of the MySQL Optimizer.
Example of Usage
Here’s a simple EXPLAIN statement using FORMAT=JSON:
SET explain_json_format_version = 1;
EXPLAIN FORMAT=JSON
SELECT * FROM Artists a
JOIN Albums al ON a.ArtistId = al.ArtistId;
Result:
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "9.25"
},
"nested_loop": [
{
"table": {
"table_name": "al",
"access_type": "ALL",
"possible_keys": [
"FK_Albums_Artists"
],
"rows_examined_per_scan": 20,
"rows_produced_per_join": 20,
"filtered": "100.00",
"cost_info": {
"read_cost": "0.25",
"eval_cost": "2.00",
"prefix_cost": "2.25",
"data_read_per_join": "15K"
},
"used_columns": [
"AlbumId",
"AlbumName",
"ReleaseDate",
"ArtistId",
"GenreId"
]
}
},
{
"table": {
"table_name": "a",
"access_type": "eq_ref",
"possible_keys": [
"PRIMARY"
],
"key": "PRIMARY",
"used_key_parts": [
"ArtistId"
],
"key_length": "4",
"ref": [
"music.al.ArtistId"
],
"rows_examined_per_scan": 1,
"rows_produced_per_join": 20,
"filtered": "100.00",
"cost_info": {
"read_cost": "5.00",
"eval_cost": "2.00",
"prefix_cost": "9.25",
"data_read_per_join": "15K"
},
"used_columns": [
"ArtistId",
"ArtistName",
"ActiveFrom"
]
}
}
]
}
}
The results are in JSON format as expected.
But more to the point, this is JSON format 1. I made sure of that by starting off with SET explain_json_format_version = 1;.
Change explain_json_format_version and Run it Again
Now let’s set my explain_json_format_version variable to 2 and run the statement again:
SET explain_json_format_version = 2;
EXPLAIN FORMAT=JSON
SELECT * FROM Artists a
JOIN Albums al ON a.ArtistId = al.ArtistId;
Result:
{
"query": "/* select#1 */ select `music`.`a`.`ArtistId` AS `ArtistId`,`music`.`a`.`ArtistName` AS `ArtistName`,`music`.`a`.`ActiveFrom` AS `ActiveFrom`,`music`.`al`.`AlbumId` AS `AlbumId`,`music`.`al`.`AlbumName` AS `AlbumName`,`music`.`al`.`ReleaseDate` AS `ReleaseDate`,`music`.`al`.`ArtistId` AS `ArtistId`,`music`.`al`.`GenreId` AS `GenreId` from `music`.`artists` `a` join `music`.`albums` `al` where (`music`.`a`.`ArtistId` = `music`.`al`.`ArtistId`)",
"inputs": [
{
"alias": "al",
"operation": "Table scan on al",
"table_name": "albums",
"access_type": "table",
"schema_name": "music",
"used_columns": [
"AlbumId",
"AlbumName",
"ReleaseDate",
"ArtistId",
"GenreId"
],
"estimated_rows": 20.0,
"estimated_total_cost": 2.25
},
{
"alias": "a",
"covering": false,
"operation": "Single-row index lookup on a using PRIMARY (ArtistId=al.ArtistId)",
"index_name": "PRIMARY",
"table_name": "artists",
"access_type": "index",
"schema_name": "music",
"used_columns": [
"ArtistId",
"ArtistName",
"ActiveFrom"
],
"estimated_rows": 1.0,
"lookup_condition": "ArtistId=al.ArtistId",
"index_access_type": "index_lookup",
"estimated_total_cost": 0.255
}
],
"join_type": "inner join",
"operation": "Nested loop inner join",
"access_type": "join",
"estimated_rows": 20.0,
"join_algorithm": "nested_loop",
"estimated_total_cost": 9.25
}
The results, although still in JSON, look a bit different. That’s because it’s using JSON format 2.
Using the explain_format Variable
The FORMAT argument is optional when using the EXPLAIN statement. If we omit this argument then the explain_format system variable is used to provide the output format.
As alluded to, the explain_json_format_version system variable only applies when the output format is JSON.
So let’s set explain_format and explain_json_format_version, then run an EXPLAIN statement without the FORMAT argument:
SET explain_format = JSON;
SET explain_json_format_version = 2;
EXPLAIN SELECT * FROM Artists;
Result:
{
"query": "/* select#1 */ select `music`.`artists`.`ArtistId` AS `ArtistId`,`music`.`artists`.`ArtistName` AS `ArtistName`,`music`.`artists`.`ActiveFrom` AS `ActiveFrom` from `music`.`artists`",
"operation": "Table scan on Artists",
"table_name": "Artists",
"access_type": "table",
"schema_name": "music",
"used_columns": [
"ArtistId",
"ArtistName",
"ActiveFrom"
],
"estimated_rows": 16.0,
"estimated_total_cost": 1.85
}
I used a shorter SQL statement here in order to reduce the amount output. Either way, we can see that the explain_json_format_version system variable applies to the JSON output even when JSON is the default as specified in the explain_format variable.
When explain_json_format_version is Irrelevant
Given the explain_json_format_version variable only applies to JSON output, it becomes irrelevant whenever we use non-JSON output.
For example, if the output is TRADITIONAL then explain_json_format_version has no effect:
EXPLAIN FORMAT=TRADITIONAL
SELECT * FROM Artists;
Result:
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | Artists | NULL | ALL | NULL | NULL | NULL | NULL | 16 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
This is the same whether we explicitly specify FORMAT=TRADITIONAL or simply use the default value provided by explain_format:
SET explain_format = TRADITIONAL;
SET explain_json_format_version = 2;
EXPLAIN SELECT * FROM Artists;
Result:
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | Artists | NULL | ALL | NULL | NULL | NULL | NULL | 16 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
The output is not in JSON and so explain_json_format_version has no relevance in such cases.