세미 조인은 보통 EXISTS를 사용하는 서브쿼리의 형태로 나타나며 이러한 경우 서브 쿼리에 인덱스가 존재하지 않는다면 상당히 비효율적인데 SEMI-JOIN이 일어나도록 유도한다면 성능의 향상을 꽤 할 수 있다. 즉 인덱스 없이 EXISTS를 사용하는 쿼리라면 HASH_SJ or MERGE_SJ or NL_SJ 힌트를 이용해서 세미조인이 일어나도록 푸는 것이 좋다.
인덱스 클러스터란(Index Cluster) ?
클러스터 내의 데이터를 유지하기 위해 클러스터 인덱스라는 인덱스 사용
클러스터 인덱스는 주어진 키 값을 가진 행을 포함하고 있는 블록을 가리키는데 사용된다.
클러스터 인덱스의 구조는 보통 인덱스의 구조와 비슷하다. (보통 인덱스는 NULL 키 값을 저장하지 않지만 클러스터 인덱스는 NULL 키를 저장)
클러스터로부터 행을 저장하고 읽어 들이기 위해 오라클 서버는 주어진 키 값을 갖는 첫 행을 가리키는 클러스터 인덱스를 사용
탑크리에듀교육센터(topcredu.co.kr)에서 제공하는 자료입니다.
SQL초보에서 Schema Objects까지 25번째 자료입니다.
단일/복합(결합) 인덱스(Single Column/Composite Index),고유/비고유 인덱스(Unique/Non Unique Index), Descending Index, 함수기반 인덱스(Function Based Index), 인덱스 재구성 및 삭제, 인덱스 숨기기(Index Invisible)에 대하여 설명한 자료이오니 확인 후 많은 도움 되셨길 바랍니다^^.
머지조인(Merge Join)은 조인시 각 테이블을 따로 정렬 후 그 결과를 병합(Merge)하는 집합 연산으로 소트를 동반하므로 소트 머지 조인이라고도 한다. 배치성 JOB에는 효율적이나 OLTP성 업무에는 비효율적일 수 있는데, 모든 ROW를 FETCH한 후 처리할 때까지는 결과를 확인할 수 없는 집합연산을 하기 때문이다.
보통 조인 컬럼에 인덱스가 존재하지 않을 때 나타나며 NESTED LOOP JOIN , HASH JOIN과 달리 실행 계획상에 나타나는 테이블의 순서(드라이빙 테이블이 무엇인지)는 중요하지 않다.
INDEX 힌트는 테이블의 칼럼에 대해 생성되어 있는 인덱스를 사용할 수 있도록 해주는 힌트 구문으로 비트맵 인덱스에 대해서도 사용이 가능하지만 Bitmap Index는 INDEX_COMBINE 사용하는 것이 원칙이다.
인덱스 영역에서 인덱스가 생성된 형태대로 순방향 스캐닝 하므로 INDEX_ASC와 동일하다.
대량의 데이터라면 ORDER BY의 사용을 자제하고 인덱스를 적절히 이용하자.
FILTER 연산은 데이터 추출 시 필터링이 일어나고 있음을 알려주는 SQL ROW 연산인데 WHERE 조건 절에서 인덱스를 사용하지 못할 때 발생한다. NESTED LOOP 방식으로 해석할 수 있는데 서브쿼리라면 메인쿼리 로우를 하나씩 읽을때 마다 서브쿼리를 한 번씩 실행하는 형태이다.
FILTER OPERATION은 IN, NOT IN, EXISTS, NOT EXISTS 를 사용하는 경우 발견할 수 있는 OPERATION이며 중첩 루프조인(Nedted Loop Join)과 유사하게 움직인다. 메인쿼리의 결과에 대해 서브쿼리의 결과값을 버퍼에 임시저장해 같거나 다른 것을 찾아 나가는 방식이다. 이러한 과정이 드라이빙되는 테이블의 각 로우에 대해 일어나기 때문에 NESTED LOOP JOIN과 유사하다고 볼 수있다.
세미 조인은 보통 EXISTS를 사용하는 서브쿼리의 형태로 나타나며 이러한 경우 서브 쿼리에 인덱스가 존재하지 않는다면 상당히 비효율적인데 SEMI-JOIN이 일어나도록 유도한다면 성능의 향상을 꽤 할 수 있다. 즉 인덱스 없이 EXISTS를 사용하는 쿼리라면 HASH_SJ or MERGE_SJ or NL_SJ 힌트를 이용해서 세미조인이 일어나도록 푸는 것이 좋다.
인덱스 클러스터란(Index Cluster) ?
클러스터 내의 데이터를 유지하기 위해 클러스터 인덱스라는 인덱스 사용
클러스터 인덱스는 주어진 키 값을 가진 행을 포함하고 있는 블록을 가리키는데 사용된다.
클러스터 인덱스의 구조는 보통 인덱스의 구조와 비슷하다. (보통 인덱스는 NULL 키 값을 저장하지 않지만 클러스터 인덱스는 NULL 키를 저장)
클러스터로부터 행을 저장하고 읽어 들이기 위해 오라클 서버는 주어진 키 값을 갖는 첫 행을 가리키는 클러스터 인덱스를 사용
탑크리에듀교육센터(topcredu.co.kr)에서 제공하는 자료입니다.
SQL초보에서 Schema Objects까지 25번째 자료입니다.
단일/복합(결합) 인덱스(Single Column/Composite Index),고유/비고유 인덱스(Unique/Non Unique Index), Descending Index, 함수기반 인덱스(Function Based Index), 인덱스 재구성 및 삭제, 인덱스 숨기기(Index Invisible)에 대하여 설명한 자료이오니 확인 후 많은 도움 되셨길 바랍니다^^.
머지조인(Merge Join)은 조인시 각 테이블을 따로 정렬 후 그 결과를 병합(Merge)하는 집합 연산으로 소트를 동반하므로 소트 머지 조인이라고도 한다. 배치성 JOB에는 효율적이나 OLTP성 업무에는 비효율적일 수 있는데, 모든 ROW를 FETCH한 후 처리할 때까지는 결과를 확인할 수 없는 집합연산을 하기 때문이다.
보통 조인 컬럼에 인덱스가 존재하지 않을 때 나타나며 NESTED LOOP JOIN , HASH JOIN과 달리 실행 계획상에 나타나는 테이블의 순서(드라이빙 테이블이 무엇인지)는 중요하지 않다.
INDEX 힌트는 테이블의 칼럼에 대해 생성되어 있는 인덱스를 사용할 수 있도록 해주는 힌트 구문으로 비트맵 인덱스에 대해서도 사용이 가능하지만 Bitmap Index는 INDEX_COMBINE 사용하는 것이 원칙이다.
인덱스 영역에서 인덱스가 생성된 형태대로 순방향 스캐닝 하므로 INDEX_ASC와 동일하다.
대량의 데이터라면 ORDER BY의 사용을 자제하고 인덱스를 적절히 이용하자.
FILTER 연산은 데이터 추출 시 필터링이 일어나고 있음을 알려주는 SQL ROW 연산인데 WHERE 조건 절에서 인덱스를 사용하지 못할 때 발생한다. NESTED LOOP 방식으로 해석할 수 있는데 서브쿼리라면 메인쿼리 로우를 하나씩 읽을때 마다 서브쿼리를 한 번씩 실행하는 형태이다.
FILTER OPERATION은 IN, NOT IN, EXISTS, NOT EXISTS 를 사용하는 경우 발견할 수 있는 OPERATION이며 중첩 루프조인(Nedted Loop Join)과 유사하게 움직인다. 메인쿼리의 결과에 대해 서브쿼리의 결과값을 버퍼에 임시저장해 같거나 다른 것을 찾아 나가는 방식이다. 이러한 과정이 드라이빙되는 테이블의 각 로우에 대해 일어나기 때문에 NESTED LOOP JOIN과 유사하다고 볼 수있다.
CHOOSE 힌트는 테이블에 통계 정보가 존재한다면 CBO(COST BASED OPTIMIZER)의 ALL_ROWS로 동작을 하고 통계정보가 없다면 RBO(RULE-BASED OPTIMIZER)로 동작한다. CBO의 ALL_ROWS는 비용 기반 옵티마이저 환경에서 SQL문의 WHERE 조건을 만족하는 모든 행을 가장 빠르게 검색하는 실행 계획을 유도하며 RBO인 경우 15개 규칙에 기반한 실행계획을 수립한다. (ORACLE 11g 이후 더 이상 지원하지 않지만 여전히 사용은 가능하다.)
ANTI 조인은 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산으로 SQL연산에서 NOT IN, NOT EXISTS, MINUS 등이 있을 때 나타나는 실행계획 연산자이다. 안티 조인은 NESTED LOOP ANTI-JOIN, MERGE ANTI-JOIN 또는 HASH ANTI_JOIN으로 풀리도록 할 수 있는데 대체로 HASH ANTI_JOIN이 성능상 좋다.
SQL*Plus에서 사용자는 자동으로 Optimizer에서 실행계획과 통계정보를 얻을 수 있다. AUTOTRACE를 사용하며 DML문을 성공적으로 수행 시 만들어지며 DML문의 성능 튜닝을 위한 방법으로 자주 이용 된다.
SET AUTOTRACE를 사용하기 위해선 실행 계획용 테이블(PLAN_TABLE)이 존재해야 하며 구문을 활성화 하기 위해 SET AUTOTRACE ON, 비활성화 하기위해 SET AUTOTRACE OFF하고 하면 됩니다. 참고로 SET AUTOTRACE에서 사용 할 수 있는 옵션은 다음과 같다.
[BITMAP INDEX]
정보저장의 최소단위인 비트를 이용하여 칼럼 값을 간결하게 저장하고 이를 이용하여 자동으로 ROWID를 생성하는 구조를 가지며 분포도가 나쁜 칼럼, NOT, OR를 사용하는 경우 탁월한 성능을 낸다.
비트맵 인덱스를 생성하면 비트리 인덱스처럼 트리구조를 만들고 리프블럭에 값들을 비트로 변환하여 저장한다. 비트리 인덱스의 리프 블록(Leaf Block)은 INDEX KEY VALUE와 ROWID 로 구성이 되어 있지만 비트맵 인덱스는 START ROWID~END ROWID로 압축해서 저장하고 칼럼값 역시 ‘1’ 이라는 비트로 저장해서 원본 데이터의 ROWID를 계산한다.
인덱스를 Bit 단위로 저장(데이터의 존재 여부를 0 or 1로 표시)하고 비트리 인덱스 한계를 극복하여 대량의 자료 조회에 적합한 구조이지만 잦은 DML이 발생되는 곳은 리프 블록(Leaf Block)의 갱신으로 인해 부적합하다. 하나의 인덱스 값을 수정하면 그 인덱스 값을 가지는 모든 로우에 락을 건다. 즉 하나의 인덱스 값으로 테이블상의 여러 개의 행을 표현하기 때문에 INSERT, UPDATE, DELETE 등을 사용하는 경우 오라클 락 메커니즘인 행 단위 락(ROW LEVEL LOCKING)을 지원할 수 없다.
B*Tree 인덱스가 NULL값을 보관하지 않는 것과는 달리 Bitmap 인덱스는 NULL값에 대한 BIT값을 저장하여 비트리 인덱스의 NULL문제를 해결했으며 AND, OR 연산시 비트연산을 빠르게 수행한다.
생성 절차 : 인덱스를 생성하고자 하는 테이블 스캔을 한 후 Bitmap Index Generator에 의해 칼럼 값(비트형태의 ‘1’로 저장), 시작 ROWID, 끝 ROWID , Bitmap을 갖는 인덱스 엔트리를 생성한다. 생성된 Bitmap들을 B-tree구조에 넣기 쉽도록 key값과 start rowid 순으로 정렬하며 마지막 단계에서는 정렬된 인덱스 엔트리들을 단순히 B-tree구조로 삽입한다.
규칙 기반 옵티마이저(Rule-Based Optimizer)로 동작하여 실행 계획을 세우도록 하는 힌트인데 이 경우 테이블이나 인덱스의 통계 정보가 있다고 하더라도 무시하고 사용하지 않으며 규칙에 기반한 실행 계획을 세우게 된다.
옵티마이저는 순위가 매겨진 오퍼레이션에 근거하여 실행 계획을 세우며 순위가 높은 것이 우선 적용된다.
만약 SQL 문장에서 /*+ RULE INDEX(emp idx_ename) */ 와 같이 RULE 힌트와 다른 힌트가 같이 사용된다면 RULE 힌트만 적용되므로 주의하자.
-- 먼저 EMP에 테이블에 생성되어 있는 인덱스 및 칼럼을 확인하자.
SQL> SELECT a.index_name, a.column_name, b.visibility
FROM user_ind_columns a, user_indexes b
WHERE a.table_name = 'EMP'
AND a.index_name = b.index_name ;
-- 인덱스가 없다면 생성, 있으면 SKIP
SQL> CREATE INDEX idx_emp_job ON EMP(job);
SQL> CREATE INDEX idx_emp_deptno ON EMP(deptno);
predicate란 인덱스 접근시의 컬럼 액세스 정보, 조인 정보, filter 정보를 각 Opreation 단위로 나타낸 것이다.
access predicate : 데이터 블록을 어떤 방식으로 Access해서 읽었는지를 나타내는 것이다.
filter predicate : 데이터 블록을 읽고 나서 데이터를 어떻게 필터링 했는지를 나타낸다.
CHOOSE 힌트는 테이블에 통계 정보가 존재한다면 CBO(COST BASED OPTIMIZER)의 ALL_ROWS로 동작을 하고 통계정보가 없다면 RBO(RULE-BASED OPTIMIZER)로 동작한다. CBO의 ALL_ROWS는 비용 기반 옵티마이저 환경에서 SQL문의 WHERE 조건을 만족하는 모든 행을 가장 빠르게 검색하는 실행 계획을 유도하며 RBO인 경우 15개 규칙에 기반한 실행계획을 수립한다. (ORACLE 11g 이후 더 이상 지원하지 않지만 여전히 사용은 가능하다.)
ANTI 조인은 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산으로 SQL연산에서 NOT IN, NOT EXISTS, MINUS 등이 있을 때 나타나는 실행계획 연산자이다. 안티 조인은 NESTED LOOP ANTI-JOIN, MERGE ANTI-JOIN 또는 HASH ANTI_JOIN으로 풀리도록 할 수 있는데 대체로 HASH ANTI_JOIN이 성능상 좋다.
SQL*Plus에서 사용자는 자동으로 Optimizer에서 실행계획과 통계정보를 얻을 수 있다. AUTOTRACE를 사용하며 DML문을 성공적으로 수행 시 만들어지며 DML문의 성능 튜닝을 위한 방법으로 자주 이용 된다.
SET AUTOTRACE를 사용하기 위해선 실행 계획용 테이블(PLAN_TABLE)이 존재해야 하며 구문을 활성화 하기 위해 SET AUTOTRACE ON, 비활성화 하기위해 SET AUTOTRACE OFF하고 하면 됩니다. 참고로 SET AUTOTRACE에서 사용 할 수 있는 옵션은 다음과 같다.
[BITMAP INDEX]
정보저장의 최소단위인 비트를 이용하여 칼럼 값을 간결하게 저장하고 이를 이용하여 자동으로 ROWID를 생성하는 구조를 가지며 분포도가 나쁜 칼럼, NOT, OR를 사용하는 경우 탁월한 성능을 낸다.
비트맵 인덱스를 생성하면 비트리 인덱스처럼 트리구조를 만들고 리프블럭에 값들을 비트로 변환하여 저장한다. 비트리 인덱스의 리프 블록(Leaf Block)은 INDEX KEY VALUE와 ROWID 로 구성이 되어 있지만 비트맵 인덱스는 START ROWID~END ROWID로 압축해서 저장하고 칼럼값 역시 ‘1’ 이라는 비트로 저장해서 원본 데이터의 ROWID를 계산한다.
인덱스를 Bit 단위로 저장(데이터의 존재 여부를 0 or 1로 표시)하고 비트리 인덱스 한계를 극복하여 대량의 자료 조회에 적합한 구조이지만 잦은 DML이 발생되는 곳은 리프 블록(Leaf Block)의 갱신으로 인해 부적합하다. 하나의 인덱스 값을 수정하면 그 인덱스 값을 가지는 모든 로우에 락을 건다. 즉 하나의 인덱스 값으로 테이블상의 여러 개의 행을 표현하기 때문에 INSERT, UPDATE, DELETE 등을 사용하는 경우 오라클 락 메커니즘인 행 단위 락(ROW LEVEL LOCKING)을 지원할 수 없다.
B*Tree 인덱스가 NULL값을 보관하지 않는 것과는 달리 Bitmap 인덱스는 NULL값에 대한 BIT값을 저장하여 비트리 인덱스의 NULL문제를 해결했으며 AND, OR 연산시 비트연산을 빠르게 수행한다.
생성 절차 : 인덱스를 생성하고자 하는 테이블 스캔을 한 후 Bitmap Index Generator에 의해 칼럼 값(비트형태의 ‘1’로 저장), 시작 ROWID, 끝 ROWID , Bitmap을 갖는 인덱스 엔트리를 생성한다. 생성된 Bitmap들을 B-tree구조에 넣기 쉽도록 key값과 start rowid 순으로 정렬하며 마지막 단계에서는 정렬된 인덱스 엔트리들을 단순히 B-tree구조로 삽입한다.
규칙 기반 옵티마이저(Rule-Based Optimizer)로 동작하여 실행 계획을 세우도록 하는 힌트인데 이 경우 테이블이나 인덱스의 통계 정보가 있다고 하더라도 무시하고 사용하지 않으며 규칙에 기반한 실행 계획을 세우게 된다.
옵티마이저는 순위가 매겨진 오퍼레이션에 근거하여 실행 계획을 세우며 순위가 높은 것이 우선 적용된다.
만약 SQL 문장에서 /*+ RULE INDEX(emp idx_ename) */ 와 같이 RULE 힌트와 다른 힌트가 같이 사용된다면 RULE 힌트만 적용되므로 주의하자.
-- 먼저 EMP에 테이블에 생성되어 있는 인덱스 및 칼럼을 확인하자.
SQL> SELECT a.index_name, a.column_name, b.visibility
FROM user_ind_columns a, user_indexes b
WHERE a.table_name = 'EMP'
AND a.index_name = b.index_name ;
-- 인덱스가 없다면 생성, 있으면 SKIP
SQL> CREATE INDEX idx_emp_job ON EMP(job);
SQL> CREATE INDEX idx_emp_deptno ON EMP(deptno);
predicate란 인덱스 접근시의 컬럼 액세스 정보, 조인 정보, filter 정보를 각 Opreation 단위로 나타낸 것이다.
access predicate : 데이터 블록을 어떤 방식으로 Access해서 읽었는지를 나타내는 것이다.
filter predicate : 데이터 블록을 읽고 나서 데이터를 어떻게 필터링 했는지를 나타낸다.
JS Event Loop (Kitworks Team Study 김동현 발표)Wonjun Hwang
The document discusses the benefits of exercise for mental health. Regular physical activity can help reduce anxiety and depression and improve mood and cognitive functioning. Exercise causes chemical changes in the brain that may help protect against mental illness and improve symptoms.
Java Optional (Kitworks Team Study 김성호 발표)Wonjun Hwang
The document discusses the benefits of exercise for mental health. Regular physical activity can help reduce anxiety and depression and improve mood and cognitive functioning. Exercise causes chemical changes in the brain that may help boost feelings of calmness, happiness and focus.
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