MySQL: Explain

by Mark Nielsen
Copyright August 2023


  1. Links
  2. Setup
  3. Explain
  4. Explain Tree
  5. Explain Analyze


Links



Setup

use mark
drop table if exists test1;
create table test1 ( i int, PRIMARY KEY (i));
insert into  test1 values (10),(20),(30),(40),(50), (60);

drop table if exists test2;
create table test2 ( i int, PRIMARY KEY (i));
insert into  test2 values (10),(20);

select test1.i, test2.i                     from test1 join test2 on (test1.i = test2.i);

explain select test1.i,	test2.i             from test1 join test2 on (test1.i = test2.i)\G

explain format=tree select test1.i, test2.i from test1 join test2 on (test1.i = test2.i);

explain analyze select test1.i,	test2.i	    from test1 join test2 on (test1.i = test2.i);


Explain

mysql> explain select test1.i, test2.i             from test1 join test2 on (test1.i = test2.i)\G  
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: test2
   partitions: NULL
         type: index
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: test1
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: mark.test2.i
         rows: 1
     filtered: 100.00
        Extra: Using index
2 rows in set, 1 warning (0.00 sec)

Even though the table test1 is specified first, the optimizer chooses test2 (probably because it has fewer rows). For row in the first part, 2, is multiplied with every row in the 2nd part, 1. 2 * 1 = 2 rows. We can also see with indexes are used, it uses eq_ref for the join.


Explain Tree

  mysql> explain format=tree select test1.i, test2.i from test1 join test2 on (test1.i = test2.i);
+-----------------------------------------------------------------
| EXPLAIN                                                         
+-----------------------------------------------------------------
| -> Nested loop inner join  (cost=1.15 rows=2)
    -> Index scan on test2 using PRIMARY  (cost=0.45 rows=2)
    -> Single-row covering index lookup on test1 using PRIMARY (i=test2.i)  (cost=0.3 rows=1)
 |
+-----------------------------------------------------------------
1 row in set (0.00 sec)
We see it is a nested loop. Same thing is described, test2 is looked first and then joined to tests. 2 rows of test2 is matched with test1 of 1 row. 2 rows * 1 row is 2 rows total.

Explain Analyze

mysql> explain analyze select test1.i, test2.i     from test1 join test2 on (test1.i = test2.i);
+-----------------------------------------------------------------
| EXPLAIN                                                         
+-----------------------------------------------------------------
| -> Nested loop inner join  (cost=1.15 rows=2) (actual time=0.0247..0.0329 rows=2 loops=1)
    -> Covering index scan on test2 using PRIMARY  (cost=0.45 rows=2) (actual time=0.0147..0.0183 rows=2 loops=1)
    -> Single-row covering index lookup on test1 using PRIMARY (i=test2.i)  (cost=0.3 rows=1) (actual time=0.00577..0.00588 rows=1 loops=2)
 |
+-----------------------------------------------------------------
Same as tree, but with the time costs at each step.
  insert into test2 values (30), (40);

  mysql> explain analyze select test1.i, test2.i     from test1 join test2 on (test1.i = test2.i) order by test1.i desc;
+-----------------------------------------------------------------
| EXPLAIN                                                         
+-----------------------------------------------------------------
| -> Sort: test1.i DESC  (actual time=0.0849..0.0854 rows=4 loops=1)
    -> Stream results  (cost=2.05 rows=4) (actual time=0.0496..0.0688 rows=4 loops=1)
        -> Nested loop inner join  (cost=2.05 rows=4) (actual time=0.0479..0.0656 rows=4 loops=1)
            -> Covering index scan on test2 using PRIMARY  (cost=0.65 rows=4) (actual time=0.0316..0.0382 rows=4 loops=1)
            -> Single-row covering index lookup on test1 using PRIMARY (i=test2.i)  (cost=0.275 rows=1) (actual time=0.00591..0.00599 rows=1 loops=4)
 |
+-----------------------------------------------------------------
 
We see here in the nested loop there are 4 rows times 1 row. Then the sort the results, 4 rows.