An Introduction to the ‘explain_json_format_version’ System Variable in MySQL

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.