20. SQL 튜닝을 한다는 거는
• 옵티마이저가 최적의 비용으로 수행하게 잘 유도 하는 것
• 테이블 스캔 횟수는 최소화
20
21. 실무적인 SQL 튜닝
1. SQL문 실행결과 & 현황 파악 결과 및 소요 시간 확인
조인/서브쿼리 구조
동등/범위 조건
2. 가시적 & 비가시적 가시적 테이블의 데이터 건수
SELECT절 컬럼 분석
조건절 컬럼 분석
그루핑/정렬 컬럼
비가시적 실행계획
인덱스 현황
데이터 변경 추이
*업무적 특성*
3. 튜닝 방향 판단 & 개선/적용
21
23. 기본키를 변형하는 나쁜 SQL – ASIS (4.2.1)
SELECT *
FROM 사원
WHERE SUBSTRING(사원번호,1,4) = 1100
AND LENGTH(사원번호) = 5;
* 결과
+----------+------------+-------------+-------------+------+------------+
| 사원번호 | 생년월일 | 이름 | 성 | 성별 | 입사일자 |
+----------+------------+-------------+-------------+------+------------+
| 11000 | 1960-09-12 | Alain | Bonifati | M | 1988-08-20 |
| 11001 | 1956-04-16 | Baziley | Buchter | F | 1987-02-23 |
| 11002 | 1952-02-26 | Bluma | Ulupinar | M | 1996-12-23 |
| 11003 | 1960-11-13 | Mariangiola | Gulla | M | 1987-05-24 |
| 11004 | 1954-08-05 | JoAnna | Decleir | F | 1992-01-19 |
| 11005 | 1958-03-12 | Byong | Douceur | F | 1986-07-27 |
| 11006 | 1962-12-26 | Christoper | Butterworth | F | 1989-08-02 |
| 11007 | 1962-03-16 | Olivera | Maccarone | M | 1991-04-11 |
| 11008 | 1962-07-11 | Gennady | Menhoudj | M | 1988-09-18 |
| 11009 | 1954-08-30 | Alper | Axelband | F | 1986-09-09 |
+----------+------------+-------------+-------------+------+------------+
10 rows in set (0.21 sec)
====================================================================================
23
사원번호가 1100으로 시작하면서 사원번호가 5자리인 사원 정보
24. 기본키를 변형하는 나쁜 SQL – 실행계획 (4.2.1)
EXPLAIN
SELECT *
FROM 사원
WHERE SUBSTRING(사원번호,1,4) = 1100
AND LENGTH(사원번호) = 5;
* 결과
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered
| Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
| 1 | SIMPLE | 사원 | NULL | ALL | NULL | NULL | NULL | NULL | 299157 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
1 row in set, 1 warning (0.00 sec)
24
25. 기본키를 변형하는 나쁜 SQL – TOBE (4.2.1)
SELECT *
FROM 사원
WHERE 사원번호 BETWEEN 11000 AND 11009;
* 결과
+----------+------------+-------------+-------------+------+------------+
| 사원번호 | 생년월일 | 이름 | 성 | 성별 | 입사일자 |
+----------+------------+-------------+-------------+------+------------+
| 11000 | 1960-09-12 | Alain | Bonifati | M | 1988-08-20 |
| 11001 | 1956-04-16 | Baziley | Buchter | F | 1987-02-23 |
| 11002 | 1952-02-26 | Bluma | Ulupinar | M | 1996-12-23 |
| 11003 | 1960-11-13 | Mariangiola | Gulla | M | 1987-05-24 |
| 11004 | 1954-08-05 | JoAnna | Decleir | F | 1992-01-19 |
| 11005 | 1958-03-12 | Byong | Douceur | F | 1986-07-27 |
| 11006 | 1962-12-26 | Christoper | Butterworth | F | 1989-08-02 |
| 11007 | 1962-03-16 | Olivera | Maccarone | M | 1991-04-11 |
| 11008 | 1962-07-11 | Gennady | Menhoudj | M | 1988-09-18 |
| 11009 | 1954-08-30 | Alper | Axelband | F | 1986-09-09 |
+----------+------------+-------------+-------------+------+------------+
10 rows in set (0.00 sec)
25
26. 형변환으로 인덱스를 활용하지 못하는 나쁜 SQL – ASIS (4.2.3)
* SQL
SELECT COUNT(1)
FROM 급여
WHERE 사용여부 = 1;
* 결과
+----------+
| COUNT(1) |
+----------+
| 42842 |
+----------+
1 row in set (0.15 sec)
26
유효한 급여의 전체 개수 조회
27. 형변환으로 인덱스를 활용하지 못하는 나쁜 SQL – ASIS (4.2.3)
desc 급여;
* 결과
+----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| 사원번호 | int | NO | PRI | NULL | |
| 연봉 | int | NO | | NULL | |
| 시작일자 | date | NO | PRI | NULL | |
| 종료일자 | date | NO | | NULL | |
| 사용여부 | char(1) | YES | MUL | | |
+----------+---------+------+-----+---------+-------+
5 rows in set (0.01 sec)
27
28. 형변환으로 인덱스를 활용하지 못하는 나쁜 SQL – TOBE (4.2.3)
====================================================================================
* SQL
SELECT COUNT(1)
FROM 급여
WHERE 사용여부 = '1';
* 결과
+----------+
| COUNT(1) |
+----------+
| 42842 |
+----------+
1 row in set (0.01 sec)
28
29. 열을 결합하여 사용하는 나쁜 SQL – ASIS, 실행계획 (4.2.4)
* SQL
EXPLAIN
SELECT *
FROM 사원
WHERE CONCAT(성별,' ',성) = 'M Radwan';
* 결과
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered
| Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
| 1 | SIMPLE | 사원 | NULL | ALL | NULL | NULL | NULL | NULL | 299157 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+------
----+-------------+
1 row in set, 1 warning (0.00 sec)
29
30. 열을 결합하여 사용하는 나쁜 SQL – TOBE, 실행계획 (4.2.4)
* SQL
EXPLAIN
SELECT *
FROM 사원
WHERE 성별 = 'M'
AND 성 = 'Radwan';
* 결과
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+----
---+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+----
---+
| 1 | SIMPLE | 사원 | NULL | ref | I_성별_성 | I_성별_성 | 51 | const,const | 102 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+----
---+
1 row in set, 1 warning (0.00 sec)
30
31. 인덱스 고려 없이 열을 사용하는 SQL – ASIS (4.2.7)
* SQL
SELECT 성, 성별, COUNT(1) as 카운트
FROM 사원
GROUP BY 성, 성별;
* 결과
+------------------+------+--------+
| 성 | 성별 | 카운트 |
+------------------+------+--------+
| Aamodt | M | 120 |
| Acton | M | 108 |
| Adachi | M | 140 |
... 중략 ...
| Zwicker | F | 65 |
| Zyda | F | 72 |
| Zykh | F | 61 |
+------------------+------+--------+
3274 rows in set (0.43 sec)
31
32. 인덱스 고려 없이 열을 사용하는 SQL – 인덱스 조회 (4.2.7)
* SQL
show index from 사원;
* 결과
+-------+------------+------------+--------------+-------------+-----------+-------------
+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality |
Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+------------+--------------+-------------+-----------+-------------
+----------+--------+------+------------+---------+---------------+---------+------------+
| 사원 | 0 | PRIMARY | 1 | 사원번호 | A | 299157 | NULL | NULL | | BTREE | | | YES | NULL |
| 사원 | 1 | I_입사일자 | 1 | 입사일자 | A | 4612 | NULL | NULL | | BTREE | | | YES | NULL |
| 사원 | 1 | I_성별_성 | 1 | 성별 | A | 1 | NULL | NULL | | BTREE | | | YES | NULL |
| 사원 | 1 | I_성별_성 | 2 | 성 | A | 3257 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+------------+--------------+-------------+-----------+-------------
+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.00 sec)
32
33. 인덱스 고려 없이 열을 사용하는 SQL – TOBE (4.2.7)
* SQL
SELECT 성, 성별, COUNT(1) as 카운트
FROM 사원
GROUP BY 성별, 성;
* 결과
+------------------+------+--------+
| 성 | 성별 | 카운트 |
+------------------+------+--------+
| Aamodt | M | 120 |
| Acton | M | 108 |
| Adachi | M | 140 |
... 중략 ...
| Zwicker | F | 65 |
| Zyda | F | 72 |
| Zykh | F | 61 |
+------------------+------+--------+
3274 rows in set (0.04 sec)
33
34. 습관적으로 중복을 제거하는 나쁜 SQL – ASIS, 실행계획 (4.2.5)
EXPLAIN
SELECT DISTINCT 사원.사원번호, 이름, 성, 부서번호
FROM 사원
JOIN 부서관리자
ON (사원.사원번호 = 부서관리자. 사원번호);
* 결과
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+------------------------------+
| 1 | SIMPLE | 부서관리자 | NULL | index | PRIMARY | I_부서번호 | 12 | NULL | 24 | 100.00 | Using index; Using temporary |
| 1 | SIMPLE | 사원 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | tuning.부서관리자.사원번호 | 1 | 100.00 | NULL |
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+------------------------------+
2 rows in set, 1 warning (0.00 sec)
34
35. 습관적으로 중복을 제거하는 나쁜 SQL – TOBE, 실행계획 (4.2.5)
EXPLAIN
SELECT 사원.사원번호, 이름, 성, 부서번호
FROM 사원
JOIN 부서관리자
ON (사원.사원번호 = 부서관리자. 사원번호);
* 결과
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+-------------+
| 1 | SIMPLE | 부서관리자 | NULL | index | PRIMARY | I_부서번호 | 12 | NULL | 24 | 100.00 | Using index |
| 1 | SIMPLE | 사원 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | tuning.부서관리자.사원번호 | 1 | 100.00 | NULL |
+----+-------------+------------+------------+--------+---------------+------------+---------+----------------------------
+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
35
36. 다수의 쿼리를 UNION 연산자로 합치는 나쁜 SQL – ASIS, 실행계획
(4.2.6)
EXPLAIN
SELECT 'M' AS 성별, 사원번호
FROM 사원
WHERE 성별 = 'M'
AND 성 ='Baba'
UNION
SELECT 'F', 사원번호
FROM 사원
WHERE 성별 = 'F'
AND 성 = 'Baba';
* 결과
+------+--------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+--------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------+
| 1 | PRIMARY | 사원 | NULL | ref | I_성별_성 | I_성별_성 | 51 | const,const | 135 | 100.00 | Using index |
| 2 | UNION | 사원 | NULL | ref | I_성별_성 | I_성별_성 | 51 | const,const | 91 | 100.00 | Using index |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+------+--------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
36
37. 다수의 쿼리를 UNION 연산자로 합치는 나쁜 SQL – TOBE, 실행계획
(4.2.6)
EXPLAIN
SELECT 'M' as 성별, 사원번호
FROM 사원
WHERE 성별 = 'M'
AND 성 ='Baba'
UNION ALL
SELECT 'F' as 성별, 사원번호
FROM 사원
WHERE 성별 = 'F'
AND 성 ='Baba';
* 결과
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-------------+
| 1 | PRIMARY | 사원 | NULL | ref | I_성별_성 | I_성별_성 | 51 | const,const | 135 | 100.00 | Using index |
| 2 | UNION | 사원 | NULL | ref | I_성별_성 | I_성별_성 | 51 | const,const | 91 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
37
38. 불필요한 조인을 수행하는 나쁜 SQL – ASIS, 실행계획 (4.3.3)
* SQL
EXPLAIN
SELECT COUNT(DISTINCT 사원.사원번호) as 데이터건수
FROM 사원,
( SELECT 사원번호
FROM 사원출입기록 기록
WHERE 출입문 = 'A'
) 기록
WHERE 사원.사원번호 = 기록.사원번호;
* 결과
+----+-------------+-------+------------+--------+---------------+----------+---------+----------------------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+----------+---------+----------------------+--------+----------+-------------+
| 1 | SIMPLE | 기록 | NULL | ref | I_출입문 | I_출입문 | 4 | const | 329467 | 100.00 | Using index |
| 1 | SIMPLE | 사원 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | tuning.기록.사원번호 | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+----------+---------+----------------------+--------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
38
39. 불필요한 조인을 수행하는 나쁜 SQL – TOBE, 실행계획 (4.3.3)
• * SQL
• EXPLAIN
• SELECT COUNT(1) as 데이터건수
• FROM 사원
• WHERE EXISTS (SELECT 1
• FROM 사원출입기록 기록
• WHERE 출입문 = 'A'
• AND 기록.사원번호 = 사원.사원번호);
• * 결과
• +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------------+--------+-----
-----+--------------------------+
• | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
• +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------------+--------+-----
-----+--------------------------+
• | 1 | SIMPLE | 사원 | NULL | index | PRIMARY | I_입사일자 | 3 | NULL | 299157 | 100.00 | Using where; Using index |
• | 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 4 | tuning.사원.사원번호 | 1 | 100.00 | NULL |
• | 2 | MATERIALIZED | 기록 | NULL | ref | I_출입문 | I_출입문 | 4 | const | 329467 | 100.00 | Using index |
• +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------------+--------+-----
-----+--------------------------+
• 3 rows in set, 2 warnings (0.00 sec)
39