In MySQL we can use the DESCRIBE
statement to get information about table structure in the database, or to obtain a query execution plan for a query. For example, we can get information about column names, their data types, default values, etc.
The DESC
and EXPLAIN
statements are synonyms of DESCRIBE
, so we can alternatively use those to do the same thing.
Example
Here’s a quick example to demonstrate:
DESCRIBE t1;
Result:
+-------+-------------+------+-----+---------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-----------------------------+ | c1 | varchar(20) | YES | | NULL | | | c2 | varchar(20) | YES | | NULL | | | c3 | varchar(20) | YES | | NULL | VIRTUAL GENERATED INVISIBLE | +-------+-------------+------+-----+---------+-----------------------------+
Here I used DESCRIBE
to get information about a table called t1
. We can see that this table contains three columns called c1
, c2
, and c3
, and we get certain information about those columns.
The Extra
column provides extra information. We can see that the c3
column is a generated column. We can also see that it’s an invisible column.
Specifying the Column Name
We have the option of specifying a column name within the table. When we do this, we only get information about that specific column:
DESCRIBE t1 c3;
Result:
+-------+-------------+------+-----+---------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-----------------------------+ | c3 | varchar(20) | YES | | NULL | VIRTUAL GENERATED INVISIBLE | +-------+-------------+------+-----+---------+-----------------------------+
In this example I narrowed it down to just the c3
column.
Get a Query Execution Plan
We can also use the DESCRIBE
statement to get information about queries and other SQL statements (specifically the SELECT
, DELETE
, INSERT
, REPLACE
, TABLE
, and UPDATE
statements).
This usage is commonly assigned to the EXPLAIN
statement, but as mentioned, that’s merely a synonym for DESCRIBE
, so we can use them interchangeably.
Here’s an example of using DESCRIBE
to get a query execution plan:
DESCRIBE SELECT * FROM t1;
Result:
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 7 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
We can get additional information by checking the warnings. In this case there was just one warning. Let’s check it:
SHOW WARNINGS;
Result:
+-------+------+------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `pethotel`.`t1`.`c1` AS `c1`,`pethotel`.`t1`.`c2` AS `c2` from `pethotel`.`t1` | +-------+------+------------------------------------------------------------------------------------------------------+
This shows us a query that explicitly names each qualified column that was selected by our query. Note that the third column (c3
) isn’t included here. That’s because we used the asterisk wildcard (*
) in our query to select all columns. When we do this, invisible columns are not included. The c3
column in our table is an invisible column, as we saw in the first example when we used DESCRIBE
to get the table information.
Specify the Format
We can use the FORMAT
argument to specify the output format. As of this writing, the valid formats are TRADITIONAL
, JSON
, and TREE
.
Here’s an example of setting the format to JSON
:
DESCRIBE FORMAT=JSON SELECT * FROM t1;
Result:
{ "query_block": { "select_id": 1, "cost_info": { "query_cost": "0.95" }, "table": { "table_name": "t1", "access_type": "ALL", "rows_examined_per_scan": 7, "rows_produced_per_join": 7, "filtered": "100.00", "cost_info": { "read_cost": "0.25", "eval_cost": "0.70", "prefix_cost": "0.95", "data_read_per_join": "1K" }, "used_columns": [ "c1", "c2" ] } } }
The default format is TRADITIONAL
, but this will depend on your explain_format
system variable if you have it (this system variable was introduced in MySQL 8.0.32). If your explain_format
variable is set to a value other than TRADITIONAL
, then the default format will be whatever that value is (assuming it’s a valid value).
We can check the value of the explain_format
system variable like this:
SELECT @@explain_format;
Result:
+------------------+ | @@explain_format | +------------------+ | TRADITIONAL | +------------------+
In my case it’s set to the default value of TRADITIONAL
.
The DESC
Statement
As mentioned DESC
is a synonym for DESCRIBE
. Therefore we can replace DESCRIBE
with DESC
to get the same output.
Example:
DESC t1;
Result:
+-------+-------------+------+-----+---------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-----------------------------+ | c1 | varchar(20) | YES | | NULL | | | c2 | varchar(20) | YES | | NULL | | | c3 | varchar(20) | YES | | NULL | VIRTUAL GENERATED INVISIBLE | +-------+-------------+------+-----+---------+-----------------------------+
We got the same result that we got when using DESCRIBE
.
DESCRIBE
for a Certain Connection
We can use DESCRIBE
to get the execution plan for the statement being executed at that connection.
For example:
DESCRIBE FOR CONNECTION 128;
That assumes there’s a connection with a connection ID of 128, and that it’s executing a statement.
If we specify a connection ID that doesn’t exist, we’ll get an “unknown thread” error:
ERROR 1094 (HY000): Unknown thread id: 128
We can use the following command to get a list of connections:
SHOW PROCESSLIST;
DESCRIBE
for a Certain Database
As from MySQL 8.3, we can use FOR SCHEMA
or its synonym FOR DATABASE
to specify a database to apply the DESCRIBE
statement to.
For example:
DESCRIBE FOR DATABASE sakila
SELECT * FROM actor;
That example applies the given SELECT
against the sakila database, even though we could be in a different database.
In this case I used FOR DATABASE
, which is a synonym of FOR SCHEMA
.
About DESCRIBE ANALYZE
From MySQL 8.0.18 we can use DESCRIBE ANALYZE
to get DESCRIBE
output along with timing and additional, iterator-based, information about how the optimiser’s expectations matched the actual execution.
We can use DESCRIBE ANALYZE
on SELECT
and TABLE
statements, as well as with multi-table UPDATE
and DELETE
statements.
Example:
DESCRIBE ANALYZE
SELECT * FROM actor a
JOIN film_actor fa
ON a.actor_id = fa.actor_id
JOIN film f
ON f.film_id = fa.film_id
WHERE last_name LIKE 'Pit%';
Result:
-> Nested loop inner join (cost=15.8 rows=27.3) (actual time=0.105..0.343 rows=31 loops=1) -> Nested loop inner join (cost=4.39 rows=27.3) (actual time=0.0836..0.115 rows=31 loops=1) -> Index range scan on a using idx_actor_last_name over ('Pit' <= last_name <= 'Pit????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????'), with index condition: (a.last_name like 'Pit%') (cost=0.71 rows=1) (actual time=0.0454..0.0496 rows=1 loops=1) -> Index lookup on fa using PRIMARY (actor_id=a.actor_id) (cost=3.68 rows=27.3) (actual time=0.0367..0.0578 rows=31 loops=1) -> Single-row index lookup on f using PRIMARY (film_id=fa.film_id) (cost=0.322 rows=1) (actual time=0.00683..0.00691 rows=1 loops=31)
The DESCRIBE ANALYZE
statement only supports the TREE
format. Each node represents an iterator. Each iterator outputs the estimated execution cost, the estimated number of returned rows, the time to return first row, time spent (in milliseconds) executing this iterator (including child iterators, but not parent iterators), and the number of rows returned by the iterator.
As of MySQL 8.0.32, if the explain_format
system variable (which was introduced in MySQL 8.0.32) is set to TRADITIONAL
, DEFAULT
or TREE
, then TREE
is used. If the value is JSON
, then an error is returned, unless FORMAT=TREE
is specified as part of the statement.
I didn’t get an error in the above example because my explain_format
system variable is set to TRADITIONAL
. Therefore, it used TREE
. But if my explain_format
variable was set to JSON
, then I would’ve received an error.
Privileges
DESCRIBE
requires the same privileges required to execute the explained statement. It also requires the SHOW VIEW
privilege for any explained view.
Also DESCRIBE … FOR CONNECTION
requires the PROCESS
privilege if the specified connection belongs to a different user.
More Information
See the MySQL documentation for EXPLAIN
, which provides more information about the DESCRIBE
/ DESC
/ EXPLAIN
statement.