개요

쿼리 실행 절차

구분단계실행 주체결과물
1SQL 파싱SQL 파서(MariaDB 엔진)SQL 파스 트리
2최적화 및 실행 계획 수립옵티마이저(MariaDB 엔진)실행 계획
3SQL 실행스토리지 엔진 + MariaDB 엔진쿼리 결과값


옵티마이저의 종류

규칙 기반 최적화
(RBO:Rule-based optimizer)
옵티마이저에 내장된 우선순위에 따라 실행 계획을 수립
비용 기반 최적화
(CBO:Cost-based optimizer)
부하 정보와 통계 정보를 이용해 각 실행 계획별 비용을 산출하여 최소 비용의 실행 계획을 선택


MariaDB 10.0의 통계 정보

  • MariaDB 10.0부터 통합 통계 정보 관리 기능을 제공
  • 인덱스 컬럼 외 컬럼의 통계 정보 관리 가능
  • 통계 정보를 영구적으로 사용 가능: 통계 정보를 별도로 백업해 복구해서 사용할 수 있음
  • 관리자가 직접 통계 정보 변경 가능


통합 통계 테이블
table_stat컬럼의미
db_name대상 테이블이 속한 데이터베이스 명
table_name대상 테이블의 이름
cardianlity테이블의 레코드 건수
column_stat컬럼의미
db_name대상 테이블이 속한 데이터베이스 명
table_name대상 테이블의 이름
column_name대상 컬럼 이름
min_value해당 컬럼의 최소값(정수 타입도 문자열 포맷으로 저장)
max_value해당 컬럼의 최대값(정수 타입도 문자열 포맷으로 저장)
nulls_ratioNULL 값의 비율(0:NULL 없음, 0.5:NULL값을 가진 레코드가 50%, 1: 모든 레코드가 NULL)
avg_length컬럼 값의 평균 바이트 수
avg_frequency중복된 값을 가진 평균 레코드의 수(1:중복된 값 없음)
index_stat컬럼의미
db_name대상 테이블이 속한 데이터베이스 명
table_name대상 테이블의 이름
index_name대상 인덱스의 이름
prefix_arity인덱스 컬럼 순번
avg_frequency중복된 값을 가진 평균 레코드의 수(1:중복된 값 없음)


통계 파라미터
파라미터내용
use_stat_tablesneverMySQL 5.6의 통계 정보 관리 방식과 동일, 통합 통계 테이블에는 통계를 수집하지 않음
use_stat_tablescomplementary각 스토리지 엔진이 제공하는 통계정보를 우선적으로 사용하되 스토리지 엔진이 제공하는 정보가 부족하거나 없는 경우에는 MariaDB의 통합 정보가 사용됨
use_stat_tablespreferably각 스토리지 엔진별로 관리되는 통계 정보보다 MariaDB의 통합 통계 정보를 우선해서 사용

create table tabl_recalc as select * from employees;

insert into tabl_recalc select * from employees;

MariaDB [(none)]> show global variables like 'innodb_stats_auto_recalc';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_stats_auto_recalc | ON    |
+--------------------------+-------+
1 row in set (0.00 sec)

MariaDB [employees]> select * from mysql.table_stats where table_name='tabl_recalc';
Empty set (0.00 sec)

MariaDB [employees]> select * from mysql.innodb_table_stats where table_name='tabl_recalc';
+---------------+-------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name  | last_update         | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+-------------+---------------------+--------+----------------------+--------------------------+
| employees     | tabl_recalc | 2017-04-14 21:33:55 | 299439 |                 1057 |                        0 |
+---------------+-------------+---------------------+--------+----------------------+--------------------------+
1 row in set (0.00 sec)

MariaDB [employees]> show global variables like 'use_stat_tables';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| use_stat_tables | NEVER |
+-----------------+-------+
1 row in set (0.00 sec)

MariaDB [employees]> analyze table tabl_recalc;
+-----------------------+---------+----------+----------+
| Table                 | Op      | Msg_type | Msg_text |
+-----------------------+---------+----------+----------+
| employees.tabl_recalc | analyze | status   | OK       |
+-----------------------+---------+----------+----------+
1 row in set (0.01 sec)

MariaDB [employees]> select * from mysql.table_stats where table_name='tabl_recalc';
Empty set (0.00 sec)

MariaDB [employees]> SET SESSION use_stat_tables='COMPLEMENTARY';
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> analyze table tabl_recalc;
+-----------------------+---------+----------+-----------------------------------------+
| Table                 | Op      | Msg_type | Msg_text                                |
+-----------------------+---------+----------+-----------------------------------------+
| employees.tabl_recalc | analyze | status   | Engine-independent statistics collected |
| employees.tabl_recalc | analyze | status   | OK                                      |
+-----------------------+---------+----------+-----------------------------------------+
2 rows in set (0.54 sec)

MariaDB [employees]> select * from mysql.table_stats where table_name='tabl_recalc';
+-----------+-------------+-------------+
| db_name   | table_name  | cardinality |
+-----------+-------------+-------------+
| employees | tabl_recalc |      300024 |
+-----------+-------------+-------------+
1 row in set (0.00 sec)

통계 수집 시 주의 사항

ANALYZE TABLE 명령이 실행되면 테이블을 풀 스캔하거나 인덱스 풀 스캔하여 통계 수집
마스터와 슬레이브 구조라면 슬레이브에서 통계 정보를 수집하여 마스터에 복사


통계 생성 구문
tbl 테이블, col1과 col2 컬럼, idx1과 idx2 인덱스에 대해서만 통계 정보 수집
analyze table tbl persistent for columns (col1, col2) indexes (idx1, idx2);
tbl 테이블, col1과 col2 컬럼에 대해서만 통계 수집
analyze table tbl persistent for columns (col1, col2) indexes ();
tbl 테이블, idx1, idx2 인덱스에 대해서만 통계 수집
analyze table tbl persistent for columns () indexes (idx1, idx2);
tbl 테이블에 대해서만 통계 수집
analyze table tbl persistent for columns () indexes ();
테이블, 모든 컬럼, 모든 인덱스의 통계 수집(=analyze table tbl; )
analyze table tbl persistent for all;


히스토그램 통계 정보

  • 히스토그램: 컬럼 값의 분포도를 분석할 수 있는 통계 정보
  • Height-Balanced Histogram 알고리즘 사용

MariaDB [employees]> desc mysql.column_stats;
+---------------+-----------------------------------------+------+-----+---------+-------+
| Field         | Type                                    | Null | Key | Default | Extra |
+---------------+-----------------------------------------+------+-----+---------+-------+
| db_name       | varchar(64)                             | NO   | PRI | NULL    |       |
| table_name    | varchar(64)                             | NO   | PRI | NULL    |       |
| column_name   | varchar(64)                             | NO   | PRI | NULL    |       |
| min_value     | varbinary(255)                          | YES  |     | NULL    |       |
| max_value     | varbinary(255)                          | YES  |     | NULL    |       |
| nulls_ratio   | decimal(12,4)                           | YES  |     | NULL    |       |
| avg_length    | decimal(12,4)                           | YES  |     | NULL    |       |
| avg_frequency | decimal(12,4)                           | YES  |     | NULL    |       |
| hist_size     | tinyint(3) unsigned                     | YES  |     | NULL    |       |
| hist_type     | enum('SINGLE_PREC_HB','DOUBLE_PREC_HB') | YES  |     | NULL    |       |
| histogram     | varbinary(255)                          | YES  |     | NULL    |       |
+---------------+-----------------------------------------+------+-----+---------+-------+
11 rows in set (0.00 sec)

--MariaDB에서 히스토그램 사용
MariaDB [employees]> set histogram_size=20;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> set use_stat_tables='preferably';
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> set histogram_type='double_prec_hb';
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> analyze table salaries;
+--------------------+---------+----------+-----------------------------------------+
| Table              | Op      | Msg_type | Msg_text                                |
+--------------------+---------+----------+-----------------------------------------+
| employees.salaries | analyze | status   | Engine-independent statistics collected |
| employees.salaries | analyze | status   | OK                                      |
+--------------------+---------+----------+-----------------------------------------+
2 rows in set (8.28 sec)

MariaDB [employees]> select table_name, min_value, max_value, hist_size, hist_type,
    -> decode_histogram(hist_type, histogram) as histogram
    -> from mysql.column_stats
    -> where table_name='salaries' and column_name='salary'\G
*************************** 1. row ***************************
table_name: salaries
 min_value: 38623
 max_value: 158220
 hist_size: 20
 hist_type: DOUBLE_PREC_HB
 histogram: 0.04030,0.03436,0.03273,0.03198,0.03232,0.03375,0.03688,0.04285,0.05443,0.07974,0.58065    <<< 버킷최대값 / (최대값-최소값) * 216 (single_prec_hb:28)
1 row in set (0.00 sec)

MariaDB [employees]> set histogram_size=100;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> set use_stat_tables='preferably';
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> set histogram_type='double_prec_hb';
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> analyze table salaries;
+--------------------+---------+----------+-----------------------------------------+
| Table              | Op      | Msg_type | Msg_text                                |
+--------------------+---------+----------+-----------------------------------------+
| employees.salaries | analyze | status   | Engine-independent statistics collected |
| employees.salaries | analyze | status   | OK                                      |
+--------------------+---------+----------+-----------------------------------------+
2 rows in set (7.94 sec)

MariaDB [employees]> set optimizer_use_condition_selectivity=4;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> explain extended select * from salaries where to_date <= '1989-03-01';
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
| id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
|    1 | SIMPLE      | salaries | ALL  | NULL          | NULL | NULL    | NULL | 2844047 |     5.88 | Using where |
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

MariaDB [employees]> select count(*), (2844047*0.0588) as expected_value from salaries where to_date <= '1989-03-01';
+----------+----------------+
| count(*) | expected_value |
+----------+----------------+
|   125536 |    167229.9636 |
+----------+----------------+
1 row in set (2.40 sec)

MariaDB [employees]> set histogram_size=200;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> analyze table salaries;
+--------------------+---------+----------+-----------------------------------------+
| Table              | Op      | Msg_type | Msg_text                                |
+--------------------+---------+----------+-----------------------------------------+
| employees.salaries | analyze | status   | Engine-independent statistics collected |
| employees.salaries | analyze | status   | OK                                      |
+--------------------+---------+----------+-----------------------------------------+
2 rows in set (9.70 sec)

MariaDB [employees]> explain extended
    -> select * from salaries where to_date <= '1989-03-01';
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
| id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
|    1 | SIMPLE      | salaries | ALL  | NULL          | NULL | NULL    | NULL | 2844047 |     4.95 | Using where |
+------+-------------+----------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

MariaDB [employees]> select count(*), (2844047*0.0495) as expected_value from salaries where to_date <= '1989-03-01';
+----------+----------------+
| count(*) | expected_value |
+----------+----------------+
|   125536 |    140780.3265 |
+----------+----------------+
1 row in set (2.83 sec)


조인 옵티마이저 옵션

  • Exhaustive 검색: from절에 명시된 모든 테이블 조합에 대해서 실행 계획의 비용을 계산하여 최적의 조합 선택
  • Heuristic 검색(Greedy 검색)
구분내용
1전체 N개의 테이블 중에서 optimizer_search_depth 시스템 설정 변수에 정의된 개수의 테이블로 가능한 조인 조합을 생성
21번에서 생성된 조인 조합 중에서 최소 비용의 실행 계획 하나를 선정
32번에서 선정된 실행 계획의 첫 번째 테이블을 "부분 실행 계획"의 첫번째 테이블로 선정
4전체 N-1개의 테이블 중에서(3번에서 선택된 테이블 제외) optimizer_search_depth 시스템 변수에 정의된 개수의 테이블로 가능한 조인 조합을 생성
54번에서 생성된 조인 조합들을 하나씩 3번에서 생성된 "부분 실행 계획"에 대입해서 실행 비용을 계산
65번의 비용 계산 결과, 가장 비용이 낮은 실행 계획을 골라서 그 중 두 번째 테이블을 3번에서 생성된 "부분 실행 계획"의 두 번째 테이블로 선정
7남은 테이블이 모두 없어질 때까지 4~6번까지의 과정을 반복 실행하면서 "부분 실행 계획"에 테이블의 조인 순서를 기록
8최종적으로 "부분 실행 계획"이 테이블의 조인 순서로 결정
  • optimizer_search_depth의 default 값: 64

MariaDB [employees]> set session optimizer_prune_level=1;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> set session optimizer_search_depth=5;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> select * from tab01,tab02,tab03,tab04,tab05,tab06,tab07,tab08,tab09,tab10, tab11,tab12,tab13,tab14,tab15,tab16,tab17,tab18,tab19,tab20, tab21,tab22,tab23,tab24,tab25,tab26,tab27,tab28,tab29,tab30 where tab01.fd1=tab02.fd1 and tab02.fd1=tab03.fd2 and tab03.fd1=tab04.fd1 and tab04.fd2=tab05.fd2 and tab05.fd2=tab06.fd1 and tab06.fd2=tab07.fd2 and tab07.fd1=tab08.fd1 and tab08.fd1=tab09.fd2 and tab09.fd1=tab10.fd1 and tab10.fd2=tab11.fd2 and tab11.fd2=tab12.fd1 and tab12.fd2=tab13.fd2 and tab13.fd1=tab14.fd1 and tab14.fd1=tab15.fd2 and tab15.fd1=tab16.fd1 and tab16.fd2=tab17.fd2 and tab17.fd2=tab18.fd1 and tab18.fd2=tab19.fd2 and tab19.fd1=tab20.fd1 and tab20.fd1=tab21.fd2 and tab21.fd1=tab22.fd1 and tab22.fd2=tab23.fd2 and tab23.fd2=tab24.fd1 and tab24.fd2=tab25.fd2 and tab25.fd1=tab26.fd1 and tab26.fd1=tab27.fd2 and tab27.fd1=tab28.fd1 and tab28.fd2=tab29.fd2 and tab29.fd2=tab30.fd1;
(((결과 생략)))
2000 rows in set (0.09 sec)

MariaDB [employees]> set session optimizer_search_depth=64;
Query OK, 0 rows affected, 2 warnings (0.00 sec)

MariaDB [employees]> reset query cache;
Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> select * from tab01,tab02,tab03,tab04,tab05,tab06,tab07,tab08,tab09,tab10, tab11,tab12,tab13,tab14,tab15,tab16,tab17,tab18,tab19,tab20, tab21,tab22,tab23,tab24,tab25,tab26,tab27,tab28,tab29,tab30 where tab01.fd1=tab02.fd1 and tab02.fd1=tab03.fd2 and tab03.fd1=tab04.fd1 and tab04.fd2=tab05.fd2 and tab05.fd2=tab06.fd1 and tab06.fd2=tab07.fd2 and tab07.fd1=tab08.fd1 and tab08.fd1=tab09.fd2 and tab09.fd1=tab10.fd1 and tab10.fd2=tab11.fd2 and tab11.fd2=tab12.fd1 and tab12.fd2=tab13.fd2 and tab13.fd1=tab14.fd1 and tab14.fd1=tab15.fd2 and tab15.fd1=tab16.fd1 and tab16.fd2=tab17.fd2 and tab17.fd2=tab18.fd1 and tab18.fd2=tab19.fd2 and tab19.fd1=tab20.fd1 and tab20.fd1=tab21.fd2 and tab21.fd1=tab22.fd1 and tab22.fd2=tab23.fd2 and tab23.fd2=tab24.fd1 and tab24.fd2=tab25.fd2 and tab25.fd1=tab26.fd1 and tab26.fd1=tab27.fd2 and tab27.fd1=tab28.fd1 and tab28.fd2=tab29.fd2 and tab29.fd2=tab30.fd1;
(((결과 생략)))
2000 rows in set (0.10 sec)


실행 계획 분석


MariaDB [employees]> explain
    -> select e.emp_no, e.first_name, s.from_date, s.salary
    -> from employees e, salaries s
    -> where e.emp_no = s.emp_no
    -> limit 10;
+------+-------------+-------+-------+---------------+--------------+---------+--------------------+--------+-------------+
| id   | select_type | table | type  | possible_keys | key          | key_len | ref                | rows   | Extra       |
+------+-------------+-------+-------+---------------+--------------+---------+--------------------+--------+-------------+
|    1 | SIMPLE      | e     | index | PRIMARY       | ix_firstname | 16      | NULL               | 299335 | Using index |
|    1 | SIMPLE      | s     | ref   | PRIMARY       | PRIMARY      | 4       | employees.e.emp_no |      4 |             |
+------+-------------+-------+-------+---------------+--------------+---------+--------------------+--------+-------------+
2 rows in set (0.00 sec)

  • 레코드는 쿼리 문장에서 사용된 테이블의 개수만큼 출력(임시테이블 포함)
  • 실행 순서는 위에서 아래로 순서대로 표시(union이나 상관 서브 쿼리와 같은 경우 순서대로 표시되지 않을 수 있음)
  • id 컬럼의 값이 작을 수록 쿼리의 outer부분이거나 먼저 접근한 테이블, id 컬럼의 값이 클수록 쿼리의 inner부분 또는 나중에 접근한 테이블


id 컬럼

  • 단위 select 쿼리별로 부여되는 식별자

MariaDB [employees]> explain
    -> select
    -> ((select count(*) from employees) + (select count(*) from departments)) as total_count;
+------+-------------+-------------+-------+---------------+-------------+---------+------+--------+----------------+
| id   | select_type | table       | type  | possible_keys | key         | key_len | ref  | rows   | Extra          |
+------+-------------+-------------+-------+---------------+-------------+---------+------+--------+----------------+
|    1 | PRIMARY     | NULL        | NULL  | NULL          | NULL        | NULL    | NULL |   NULL | No tables used |
|    3 | SUBQUERY    | departments | index | NULL          | PRIMARY     | 4       | NULL |      9 | Using index    |
|    2 | SUBQUERY    | employees   | index | NULL          | ix_hiredate | 3       | NULL | 299335 | Using index    |
+------+-------------+-------------+-------+---------------+-------------+---------+------+--------+----------------+
3 rows in set (0.00 sec)


select_type 컬럼

SIMPLEunion이나 서브 쿼리를 사용하지 않는 단순한 select 쿼리, 실행계획에서 SIMPLE은 하나만 존재하며 일반적으로 제일 바깥 select쿼리가 SIMPLE로 표시
PRIMARYunion이나 서브 쿼리를 가지는 select 쿼리의 실행 계획에서 가장 바깥쪽(outer)에 있는 단위 쿼리, 실행계획에서 하나만 존재하며 일반적으로 제일 바깥 select쿼리가 PRIMARY로 표시
UNIONunion으로 결합하는 단위 select쿼리 가운데 첫 번째를 제외한 두 번째 이후 단위 select 쿼리
DEPENDENT UNIONunion이나 union all로 결합된 단위 쿼리가 외부의 의해 영향을 받는 쿼리
UNION RESULTunion 결과 테이블을 의미, id 값이 부여되지 않음
SUBQUERYfrom절 이외에서 사용되는 서브 쿼리만을 의미
DEPENDENT SUBQUERY서브 쿼리가 바깥쪽(outer) select 쿼리에서 정의된 컬럼을 사용하는 경우
DERIVED단위 select쿼리의 실행 결과를 메모리나 디스크에 임시 테이블을 생성하는 것을 의미, 서브 쿼리가 from절에 사용된 경우
UNCACHEABLE SUBQUERY서브 쿼리 결과를 내부적인 캐시 공간에 저장되지 못하는 경우
1. 사용자 변수가 서브 쿼리에 사용된 경우
2. not-deterministic 속성의 스토어드 루틴이 서브 쿼리 내에 사용된 경우
3. UUID()나 RAND()와 같이 결과값이 호출될 때마다 달라지는 함수가 서브 쿼리에 사용된 경우
UNCACHEABLE UNIONunion 쿼리 결과가 내부적인 캐시 공간에 저장되지 못하는 경우
MATERIALIZED서브 쿼리의 내용을 임시 테이블로 구체화(materialization)하는 경우
INSERTinsert문의 실행계획

--UNION
MariaDB [employees]> explain
    -> select tb.* from (
    -> (select emp_no from employees e1 limit 10)
    -> union all
    -> (select emp_no from employees e2 limit 10)
    -> union all
    -> (select emp_no from employees e3 limit 10)
    -> ) tb;
+------+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+
| id   | select_type | table      | type  | possible_keys | key         | key_len | ref  | rows   | Extra       |
+------+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+
|    1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL        | NULL    | NULL |     30 |             |
|    2 | DERIVED     | e1         | index | NULL          | ix_hiredate | 3       | NULL | 299335 | Using index |
|    3 | UNION       | e2         | index | NULL          | ix_hiredate | 3       | NULL | 299335 | Using index |
|    4 | UNION       | e3         | index | NULL          | ix_hiredate | 3       | NULL | 299335 | Using index |
+------+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+
4 rows in set (0.00 sec)

--DEPENDENT UNION & UNION RESULT
MariaDB [employees]> explain
    -> select *
    -> from employees e1
    -> where e1.emp_no in (
    -> select e2.emp_no from employees e2 where e2.first_name='Matt'
    -> union
    -> select e3.emp_no from employees e3 where e3.first_name='Matt'
    -> );
+------+--------------------+------------+--------+----------------------+---------+---------+------+--------+-------------+
| id   | select_type        | table      | type   | possible_keys        | key     | key_len | ref  | rows   | Extra       |
+------+--------------------+------------+--------+----------------------+---------+---------+------+--------+-------------+
|    1 | PRIMARY            | e1         | ALL    | NULL                 | NULL    | NULL    | NULL | 299335 | Using where |
|    2 | DEPENDENT SUBQUERY | e2         | eq_ref | PRIMARY,ix_firstname | PRIMARY | 4       | func |      1 | Using where |
|    3 | DEPENDENT UNION    | e3         | eq_ref | PRIMARY,ix_firstname | PRIMARY | 4       | func |      1 | Using where |
| NULL | UNION RESULT       | <union2,3> | ALL    | NULL                 | NULL    | NULL    | NULL |   NULL |             |
+------+--------------------+------------+--------+----------------------+---------+---------+------+--------+-------------+
4 rows in set (0.00 sec)

--DEPENDENT SUBQUERY
MariaDB [employees]> explain
    -> select e.first_name,
    -> (select count(*)
    -> from dept_emp de, dept_manager dm
    -> where dm.dept_no=de.dept_no and de.emp_no=e.emp_no) as cnt
    -> from employees e
    -> where e.first_name='Matt';
+------+--------------------+-------+------+---------------------------+-------------------+---------+----------------------+------+--------------------------+
| id   | select_type        | table | type | possible_keys             | key               | key_len | ref                  | rows | Extra                    |
+------+--------------------+-------+------+---------------------------+-------------------+---------+----------------------+------+--------------------------+
|    1 | PRIMARY            | e     | ref  | ix_firstname              | ix_firstname      | 16      | const                |  233 | Using where; Using index |
|    2 | DEPENDENT SUBQUERY | de    | ref  | PRIMARY,ix_empno_fromdate | ix_empno_fromdate | 4       | employees.e.emp_no   |    1 | Using index              |
|    2 | DEPENDENT SUBQUERY | dm    | ref  | PRIMARY                   | PRIMARY           | 4       | employees.de.dept_no |    1 | Using index              |
+------+--------------------+-------+------+---------------------------+-------------------+---------+----------------------+------+--------------------------+
3 rows in set (0.00 sec)

--DERIVED
MariaDB [employees]> explain
    -> select *
    -> from (select de.emp_no from dept_emp de group by de.emp_no) tb,
    -> employees e
    -> where e.emp_no=tb.emp_no;
+------+-------------+------------+--------+---------------+-------------------+---------+-----------+--------+-------------+
| id   | select_type | table      | type   | possible_keys | key               | key_len | ref       | rows   | Extra       |
+------+-------------+------------+--------+---------------+-------------------+---------+-----------+--------+-------------+
|    1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL              | NULL    | NULL      | 331570 |             |
|    1 | PRIMARY     | e          | eq_ref | PRIMARY       | PRIMARY           | 4       | tb.emp_no |      1 |             |
|    2 | DERIVED     | de         | index  | NULL          | ix_empno_fromdate | 7       | NULL      | 331570 | Using index |
+------+-------------+------------+--------+---------------+-------------------+---------+-----------+--------+-------------+
3 rows in set (0.00 sec)

--UNCACHEABLE SUBQUERY
MariaDB [employees]> explain
    -> select *
    -> from employees e
    -> where e.emp_no = (
    -> select @status from dept_emp de where de.dept_no='d005'
    -> );
+------+----------------------+-------+-------+---------------+---------+---------+-------+--------+--------------------------+
| id   | select_type          | table | type  | possible_keys | key     | key_len | ref   | rows   | Extra                    |
+------+----------------------+-------+-------+---------------+---------+---------+-------+--------+--------------------------+
|    1 | PRIMARY              | e     | const | PRIMARY       | PRIMARY | 4       | const |      1 | Using where              |
|    2 | UNCACHEABLE SUBQUERY | de    | ref   | PRIMARY       | PRIMARY | 4       | const | 165785 | Using where; Using index |
+------+----------------------+-------+-------+---------------+---------+---------+-------+--------+--------------------------+
2 rows in set (0.01 sec)

MariaDB [employees]> explain
    -> select *
    -> from employees e
    -> where e.emp_no in (select emp_no from salaries where salary between 100 and 1000);
+------+--------------+-------------+--------+-------------------+-----------+---------+---------------------------+------+--------------------------+
| id   | select_type  | table       | type   | possible_keys     | key       | key_len | ref                       | rows | Extra                    |
+------+--------------+-------------+--------+-------------------+-----------+---------+---------------------------+------+--------------------------+
|    1 | PRIMARY      | <subquery2> | ALL    | distinct_key      | NULL      | NULL    | NULL                      |    1 |                          |
|    1 | PRIMARY      | e           | eq_ref | PRIMARY           | PRIMARY   | 4       | employees.salaries.emp_no |    1 |                          |
|    2 | MATERIALIZED | salaries    | range  | PRIMARY,ix_salary | ix_salary | 4       | NULL                      |    1 | Using where; Using index |
+------+--------------+-------------+--------+-------------------+-----------+---------+---------------------------+------+--------------------------+
3 rows in set (0.00 sec)

--INSERT
MariaDB [employees]> explain
    -> insert into employees values (1, '2014-01-01', 'Matt', 'Lee', 'M', '2014-01-02');
+------+-------------+-----------+------+---------------+------+---------+------+------+-------+
| id   | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra |
+------+-------------+-----------+------+---------------+------+---------+------+------+-------+
|    1 | INSERT      | employees | ALL  | NULL          | NULL | NULL    | NULL | NULL | NULL  |
+------+-------------+-----------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)

MariaDB [employees]> explain
    -> update employees set gender='F' where first_name='Matt';
+------+-------------+-----------+-------+---------------+--------------+---------+------+------+-------------+
| id   | select_type | table     | type  | possible_keys | key          | key_len | ref  | rows | Extra       |
+------+-------------+-----------+-------+---------------+--------------+---------+------+------+-------------+
|    1 | SIMPLE      | employees | range | ix_firstname  | ix_firstname | 16      | NULL |  233 | Using where |
+------+-------------+-----------+-------+---------------+--------------+---------+------+------+-------------+
1 row in set (0.00 sec)

    • table 컬럼
      1. 테이블 이름에 별칭이 있는 경우 실행 계획에서 별칭으로 표시
      2. 테이블이 없거나 dual 테이블 사용 시 NULL로 표시

MariaDB [employees]> explain select now();
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

MariaDB [employees]> explain select now() from dual;
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)


type 컬럼

  • 각 테이블의 레코드를 어떤 방식으로 읽었는지를 나타냄
system레코드가 1건만 존재하는 테이블 또는 한 건도 존재하는 않는 테이블을 참조하는 형태의 접근 방법, InnoDB, XtraDB 스토리지 엔진에서는 안나오고 MyISAM, MEMORY 테이블에서만 사용
const테이블의 레코드의 건수에 관계없이 쿼리가 프라이머리 키나 유니크 키 컬럼을 이용하는 where 조건절을 가지고 있으며, 반드시 1건을 반환하는 쿼리의 처리 방식
eq_ref조인에서 처음 읽은 테이블의 컬럼 값을 그 다음 읽어야 할 테이블의 프라이머리 키나 유니크 키 컬럼의 검색 조건에 사용할 때
ref조인의 순서와 인덱스의 종류에 관계없이 동등 조건으로 검색
fulltextMariaDB의 전문 검색 인덱스를 사용해 레코드를 읽는 접근 방법을 의미
ref_or_nullref 방식 또는 NULL 비교 접근 방식
unique_subquerywhere 조건절에서 사용될 수 있는 IN(sebquery)형태의 쿼리를 위한 접근 방식
index_subqueryIN(subquery)형태의 조건에서 subquery의 반환 값에 중복된 값이 있을 수 있지만 인덱스를 이용해 중복된 값을 제거
range인덱스 레인지 스캔 형태의 접근 방법
index_merge2개 이상의 인덱스를 이용해 각각의 검색 결과를 만들어낸 후 그 결과를 병합하는 처리 방식
index인덱스 풀 스캔
ALL테이블 풀 스캔

-- const
MariaDB [employees]> explain select * from employees where emp_no=10001;
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id   | select_type | table     | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
|    1 | SIMPLE      | employees | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)

--eq_ref
MariaDB [employees]> explain
    -> select * from dept_emp de, employees e
    -> where e.emp_no=de.emp_no and de.dept_no='d005';
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------+--------+-------------+
| id   | select_type | table | type   | possible_keys             | key     | key_len | ref                 | rows   | Extra       |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------+--------+-------------+
|    1 | SIMPLE      | de    | ref    | PRIMARY,ix_empno_fromdate | PRIMARY | 4       | const               | 165785 | Using where |
|    1 | SIMPLE      | e     | eq_ref | PRIMARY                   | PRIMARY | 4       | employees.de.emp_no |      1 |             |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------+--------+-------------+
2 rows in set (0.00 sec)

--ref
MariaDB [employees]> explain
    -> select * from dept_emp where dept_no='d005';
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
| id   | select_type | table    | type | possible_keys | key     | key_len | ref   | rows   | Extra       |
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
|    1 | SIMPLE      | dept_emp | ref  | PRIMARY       | PRIMARY | 4       | const | 165785 | Using where |
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
1 row in set (0.00 sec)

--fulltext
MariaDB [employees]> explain
    -> select *
    -> from employee_name
    -> where match(first_name, last_name) against ('Facello' in boolean mode);
+------+-------------+---------------+----------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table         | type     | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+---------------+----------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | employee_name | fulltext | fx_name       | fx_name | 0       |      |    1 | Using where |
+------+-------------+---------------+----------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

--ref_or_null
MariaDB [employees]> explain
    -> select *
    -> from titles where to_date='1985-03-01' or to_date is null;
+------+-------------+--------+-------------+---------------+-----------+---------+-------+------+--------------------------+
| id   | select_type | table  | type        | possible_keys | key       | key_len | ref   | rows | Extra                    |
+------+-------------+--------+-------------+---------------+-----------+---------+-------+------+--------------------------+
|    1 | SIMPLE      | titles | ref_or_null | ix_todate     | ix_todate | 4       | const |    2 | Using where; Using index |
+------+-------------+--------+-------------+---------------+-----------+---------+-------+------+--------------------------+
1 row in set (0.01 sec)

--range
MariaDB [employees]> explain
    -> select * from employees where emp_no between 10002 and 10004;
+------+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table     | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | employees | range | PRIMARY       | PRIMARY | 4       | NULL |    3 | Using where |
+------+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

--index_merge
MariaDB [employees]> explain
    -> select *
    -> from employees
    -> where emp_no between 10001 and 11000
    -> or first_name='Smith';
+------+-------------+-----------+-------------+----------------------+----------------------+---------+------+------+------------------------------------------------+
| id   | select_type | table     | type        | possible_keys        | key                  | key_len | ref  | rows | Extra                                          |
+------+-------------+-----------+-------------+----------------------+----------------------+---------+------+------+------------------------------------------------+
|    1 | SIMPLE      | employees | index_merge | PRIMARY,ix_firstname | PRIMARY,ix_firstname | 4,16    | NULL | 1000 | Using union(PRIMARY,ix_firstname); Using where |
+------+-------------+-----------+-------------+----------------------+----------------------+---------+------+------+------------------------------------------------+
1 row in set (0.00 sec)

--index
MariaDB [employees]> explain
    -> select *
    -> from departments
    -> order by dept_name desc limit 10;
+------+-------------+-------------+-------+---------------+-------------+---------+------+------+-------------+
| id   | select_type | table       | type  | possible_keys | key         | key_len | ref  | rows | Extra       |
+------+-------------+-------------+-------+---------------+-------------+---------+------+------+-------------+
|    1 | SIMPLE      | departments | index | NULL          | ux_deptname | 42      | NULL |    9 | Using index |
+------+-------------+-------------+-------+---------------+-------------+---------+------+------+-------------+
1 row in set (0.00 sec)


possibel_keys 컬럼

  • 옵티마이저가 최적의 실행 계획을 만들기 위해 후보로 선정했던 접근 방식에서 사용되는 인덱스 목록
  • "사용될 법했던 인덱스의 목록"으로 실제 그 인덱스를 사용하지는 않을 수 있음


key 컬럼

  • 최종 선택된 실행 계획에서 사용하는 인덱스를 의미
  • PRIMARY인 경우에는 프라이머리 키를 사용한다는 의미, 그 의외의 값은 테이블이나 인덱스를 생성할 때 부여했던 고유 이름


key_len 컬럼

  • 쿼리를 처리하기 위해 다중 컬럼으로 구성된 인덱스에서 몇 개의 컬럼까지 사용했는지 표시
  • 인덱스의 각 레코드에서 몇 바이트까지 사용했는지 알려주는 값

MariaDB [employees]> explain
    -> select * from dept_emp where dept_no='d005';
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
| id   | select_type | table    | type | possible_keys | key     | key_len | ref   | rows   | Extra       |
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
|    1 | SIMPLE      | dept_emp | ref  | PRIMARY       | PRIMARY | 4       | const | 165785 | Using where |
+------+-------------+----------+------+---------------+---------+---------+-------+--------+-------------+
1 row in set (0.00 sec)

MariaDB [employees]> explain
    -> select * from dept_emp where dept_no='d005' and emp_no=10001;
+------+-------------+----------+-------+---------------------------+---------+---------+-------------+------+-------+
| id   | select_type | table    | type  | possible_keys             | key     | key_len | ref         | rows | Extra |
+------+-------------+----------+-------+---------------------------+---------+---------+-------------+------+-------+
|    1 | SIMPLE      | dept_emp | const | PRIMARY,ix_empno_fromdate | PRIMARY | 8       | const,const |    1 |       |
+------+-------------+----------+-------+---------------------------+---------+---------+-------------+------+-------+
1 row in set (0.00 sec)

MariaDB [employees]> explain
    -> select * from titles where to_date <= '1985-10-10';
+------+-------------+--------+-------+---------------+-----------+---------+------+------+--------------------------+
| id   | select_type | table  | type  | possible_keys | key       | key_len | ref  | rows | Extra                    |
+------+-------------+--------+-------+---------------+-----------+---------+------+------+--------------------------+
|    1 | SIMPLE      | titles | range | ix_todate     | ix_todate | 4       | NULL |   51 | Using where; Using index |
+------+-------------+--------+-------+---------------+-----------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
-- to_date is nullable: date 3byte + null 1byte


ref 컬럼

  • 참조 조건(equal 비교 조건)으로 어떤 값이 제공됐는지 표시
  • 상수 값을 지정했다면 const로 표시, 다른 테이블의 컬럼이면 그 테이블 명과 컬럼 명이 표시

MariaDB [employees]> explain
    -> select * from employees e, dept_emp de
    -> where e.emp_no=de.emp_no;
+------+-------------+-------+--------+-------------------+---------+---------+---------------------+--------+-------+
| id   | select_type | table | type   | possible_keys     | key     | key_len | ref                 | rows   | Extra |
+------+-------------+-------+--------+-------------------+---------+---------+---------------------+--------+-------+
|    1 | SIMPLE      | de    | ALL    | ix_empno_fromdate | NULL    | NULL    | NULL                | 331570 |       |
|    1 | SIMPLE      | e     | eq_ref | PRIMARY           | PRIMARY | 4       | employees.de.emp_no |      1 |       |
+------+-------------+-------+--------+-------------------+---------+---------+---------------------+--------+-------+
2 rows in set (0.00 sec)

MariaDB [employees]> explain
    -> select * from employees e, dept_emp de
    -> where e.emp_no=(de.emp_no-1);
+------+-------------+-------+--------+---------------+---------+---------+------+--------+-------------+
| id   | select_type | table | type   | possible_keys | key     | key_len | ref  | rows   | Extra       |
+------+-------------+-------+--------+---------------+---------+---------+------+--------+-------------+
|    1 | SIMPLE      | de    | ALL    | NULL          | NULL    | NULL    | NULL | 331570 |             |
|    1 | SIMPLE      | e     | eq_ref | PRIMARY       | PRIMARY | 4       | func |      1 | Using where |
+------+-------------+-------+--------+---------------+---------+---------+------+--------+-------------+
2 rows in set (0.00 sec)


rows 컬럼

  • 실행 계획의 효율성 판단을 위해 예측했던 레코드 건수를 표시


extra 컬럼

constrow not found쿼리의 실행계획에서 const 접근 방식으로 테이블을 읽었지만 실제로 해당 테이블에 레코드가 1건도 존재하지 않는 경우
Distinct중복 제거 표시
Full scan on NUll keycol1 IN (select col2 from ...) 쿼리에서 col1이 값이 NULL이면 풀 테이블 스캔을 사용할 것을 표시
Impossible HAVING쿼리에 사용된 having절의 조건을 만족하는 레코드가 없을 때 표시, 쿼리가 제대로 작성되지 못한 경우가 대부분으로 쿼리를 다시 점검
Impossible WHEREwhere 조건이 항상 false가 될 수밖에 없는 경우
Impossible WHERE noticed after reading const tables실행 계획을 만드는 과정에서 쿼리의 일부분을 실행해 본 후 where 조건이 false인 경우
No matching min/max rowmin(), max()와 같은 집합 함수가 있는 쿼리의 조건절에 일치하는 레코드가 한 건도 없을 때 표시
No matching row in const table조인에 사용된 테이블에서 const방식으로 접근할 때 일치하는 레코드가 없는 것을 표시
No tables usedfrom절이 없는 쿼리 문장이나 "from dual"을 표시
Not existsouter조인을 이용해 안티-조인을 수행하는 쿼리 표시
Range checked for each record(index map: N)조인 조건이 모두 변수인 경우 매 레코드마다 인덱스 레인지 스캔을 표시

--Distinct
MariaDB [employees]> explain
    -> select distinct d.dept_no
    -> from departments d, dept_emp de where de.dept_no=d.dept_no;
+------+-------------+-------+-------+---------------+---------+---------+---------------------+-------+------------------------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref                 | rows  | Extra                        |
+------+-------------+-------+-------+---------------+---------+---------+---------------------+-------+------------------------------+
|    1 | SIMPLE      | d     | index | PRIMARY       | PRIMARY | 4       | NULL                |     9 | Using index; Using temporary |
|    1 | SIMPLE      | de    | ref   | PRIMARY       | PRIMARY | 4       | employees.d.dept_no | 20723 | Using index; Distinct        |
+------+-------------+-------+-------+---------------+---------+---------+---------------------+-------+------------------------------+
2 rows in set (0.00 sec)

--Full scan on NULL key
MariaDB [employees]> explain
    -> select d.dept_no, null in (select id.dept_name from departments id)
    -> from departments d;
+------+-------------+-------+----------------+---------------+-------------+---------+-------+------+-------------------------------------------------+
| id   | select_type | table | type           | possible_keys | key         | key_len | ref   | rows | Extra                                           |
+------+-------------+-------+----------------+---------------+-------------+---------+-------+------+-------------------------------------------------+
|    1 | PRIMARY     | d     | index          | NULL          | PRIMARY     | 4       | NULL  |    9 | Using index                                     |
|    2 | SUBQUERY    | id    | index_subquery | ux_deptname   | ux_deptname | 42      | const |    1 | Using index; Using where; Full scan on NULL key |
+------+-------------+-------+----------------+---------------+-------------+---------+-------+------+-------------------------------------------------+
2 rows in set (0.00 sec)

--Impossible HAVING
MariaDB [employees]> explain
    -> select e.emp_no, count(*) as cnt
    -> from employees e
    -> where e.emp_no=10001
    -> group by e.emp_no
    -> having e.emp_no is null;
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra             |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible HAVING |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------+
1 row in set (0.00 sec)

--Impossible WHERE
MariaDB [employees]> explain
    -> select * from employees where emp_no is null;
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra            |
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE |
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.00 sec)

--Impossible WHERE noticed after reading const tables
MariaDB [employees]> explain
    -> select * from employees where emp_no=0;
+------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
+------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
1 row in set (0.00 sec)

--No matching min/max row
MariaDB [employees]> explain
    -> select min(dept_no), max(dept_no)
    -> from dept_emp where dept_no='';
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                   |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No matching min/max row |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------------------+
1 row in set (0.00 sec)

--No tables used
MariaDB [employees]> explain select now();
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

MariaDB [employees]> explain select now() from dual;
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
|    1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

--Not exists
MariaDB [employees]> explain
    -> select *
    -> from dept_emp de
    -> left join departments d on de.dept_no=d.dept_no
    -> where d.dept_no is null;
+------+-------------+-------+--------+---------------+---------+---------+----------------------+--------+-------------------------+
| id   | select_type | table | type   | possible_keys | key     | key_len | ref                  | rows   | Extra                   |
+------+-------------+-------+--------+---------------+---------+---------+----------------------+--------+-------------------------+
|    1 | SIMPLE      | de    | ALL    | NULL          | NULL    | NULL    | NULL                 | 331570 |                         |
|    1 | SIMPLE      | d     | eq_ref | PRIMARY       | PRIMARY | 4       | employees.de.dept_no |      1 | Using where; Not exists |
+------+-------------+-------+--------+---------------+---------+---------+----------------------+--------+-------------------------+
2 rows in set (0.00 sec)

--Range checked for each record
MariaDB [employees]> explain select * from employees e1, employees e2 where e2.emp_no >= e1.emp_no;
+------+-------------+-------+------+---------------+------+---------+------+--------+------------------------------------------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra                                          |
+------+-------------+-------+------+---------------+------+---------+------+--------+------------------------------------------------+
|    1 | SIMPLE      | e1    | ALL  | PRIMARY       | NULL | NULL    | NULL | 299335 |                                                |
|    1 | SIMPLE      | e2    | ALL  | PRIMARY       | NULL | NULL    | NULL | 299335 | Range checked for each record (index map: 0x1) |
+------+-------------+-------+------+---------------+------+---------+------+--------+------------------------------------------------+
2 rows in set (0.00 sec)