Originally posted by **MyeoungDev** January 26, 2024
# 2장 SQL 튜닝 용어를 직관적으로 이해하기
## 2.1.1 물리 엔진과 오브젝트 용어
MySQL이라는 DBMS는 데이터를 저장하고, 저장된 데이터를 가공하는 연산을 수행한다.
1. 실행된 SQL 문을 다양한 문법 및 구문으로 검사(Parser)
2. 사용자가 요청한 데이터를 빠르고 효율적으로 찾아가는 전략적 계획 수립(Optimizer)
3. 스토리지 엔진에 위한 데이터까지 찾아간 뒤 해당 데이터를 MySQL 엔진으로 전달.
4. MySQL 엔진은 전달된 데이터에서 불필요한 부분을 필터링하고 필요한 연산을 수행한 뒤 최종 결과 전달.
### 스토리지 엔진
(InnoDB, MyISAM, Memory 등) **스토리지 엔진**은 **사용자가 요청한 SQL 문을 토대로** DB에 저장된 디스크나 메모리에 **필요한 데이터를 가져오는 역할을 수행**.
일반적으로 온라인상의 트랜잭션 발생으로 데이터를 처리하는 **OLTP(Online Transaction Processing)** 환경이 대다수인 만큼 **주로 InnoDB 엔진을 사용**한다.
**대용량 쓰기 트랜잭션이 발생**하면 **MyISAM 엔진**, **메모리 데이터를 로드**하여 빠르게 읽기 위하면 **Memory 엔진**을 사용하는 식으로 응용이 가능하다.
```sql
-- MySQL DBMS 사용중인 엔진 확인 법
SELECT ENGINE, TRANSACTIONS, COMMENT
FROM information_Schema.engines;
```
## 2.1.2 SQL 프로세스 용어
1. SQL 문을 실행하면, **파서(Parser)**는 MySQL이 **이해할 수 있는 최소 단위로 구성요소를 분리**하여 구성요소 **트리로 만든다.**
2. 트리를 만드는 과정에서 문법 오류 검토, **트리의 최소 단위**는 >, <, = 의 **기호** 혹은 **SQL 키워드**
3. **전처리기(PreProcessor)**는 생성된 **트리 결과를 토대**로, 이미 만들어진 테이블이나 뷰 등으로 구성되지 않는지, 존재하지 않은 열을 포함하지는 않는지, 조회 권한이 없는 테이블을 조회하는지 등의 **유효성 검증.**
4. **옵티마이저(Optimizer)**는 트리를 구성하는 오브젝트의 **데이터를 효율적으로 가져오기 위한** **실행 계획 수립**
5. **엔진 실행기(Engin Executor)**는 **수립된 실행 계획**으로 **스토리지 엔진을 호출**해 **데이터 가져오기.**
6. 가져온 데이터 **엔진 실행기**가 **불필요한 데이터 필터링**하여 **사용자에게 전달.**
### 옵티마이저
**DBMS의두뇌**라고 불리는 가장 핵심적인 역할.
**파서 트리를 토대**로 **필요하지 않은 조건 제거**, **연산 과정 단순화**, **테이블 접근 순서 제어**, **인덱스 사용 여부 결정**, **임시 테이블 사용 여부 결정** 등의 **실행 계획 수립.**
단, 실행 계획으로 **도출할 수 있는 경우의 수가 지나치게 많을 경우** **모든 실행 계획을 판단하지 않는다.**
이것은 **옵티마이저가 선택한 결과**가 **최상의 실행 결과는 아닐 수 있다**는 것이다.
### 엔진 실행기
**옵티마이저의 실행 계획**을 통해 **스토리지 엔진**에서 **데이터를 가져오는 역할**.
**읽어온 데이터**를 **정렬**, **조인**, **불필요한 데이터 필터링** 처리.
MySQL **엔진의 부하를 줄이려면** 스토리지 엔진에서 **가져오는 데이터양을 줄이는게 매우 중요!**
## 2.1.3 DB 오브젝트 용어
MySQL은 2차원 배열 형태로 테이블을 관리한다.
테이블은 로우(행)과 컬럼(열)을 통해서 데이터를 관리하고 표현한다.
### 기본키 Primary Key (주 키)
**특정 행을 대표하는 열을 가르키는** 용어
MySQL에서 **기본 키**는 **클러스터형 인덱스(Clustered Index)**로 작동한다.
이는 **기본 키의 구성 열 순서를 기준**으로 **물리적인 스토리지에 데이터가 쌓인다**는 뜻이다.
즉, **비슷한 기본 키 값들**이 **근거리에 적재**되므로 **기본 키를 활용**하여 **인덱스 스캔을 수행**하며 **더 빠르게 데이터 접근**이 가능하다.
> 인덱스의 주의사항
**기본 키와 똑같은 인덱스를 생성**하면 인덱스가 저장되는 **물리적 공간이 낭비**되는 한편 **데이터의 삽입, 삭제, 수정에 따른** **인덱스 정렬의 오버헤드가 발생**한다.
예시
CREATE TABLE 학생 (
학번 INT(11) NOT NULL,
이름 VARCHAR(14) NOT NULL,
…
PRIMARY KEY (학번),
INDEX I_학번 (학번)
)
이러한 인덱스의 경우 공간 낭비 & 오버헤드 발생으로 비용증가 → 삭제하자.
>
### 외래 키 Foreign Key
외래 키는 **외부에 있는 테이블을 항상 참조**하면서, **외부 테이블의 데이터가 변경**되면 **함께 영향을 받는 관계를 설정**하는 키.
연관관계가 필요한 **테이블에서의 변경사항이 발생할 때마다** **외래 키 설정조건을 항상 검증**하므로 **데이터 정합성 향상을 위해**서라도 **외래 키를 설정**해야 한다.
### 인덱스 Index
인덱스는 데이터베이스에서 키 값으로 실제 **데이터 위치를 식별하고 데이터 접근속도를 높이고자 생성되는**, **키 기준으로 정렬된 오브젝트이다.**
### 고유 인덱스 Unique Index
**인덱스를 구성하는 열들의 데이터가 유일**하다는 의미이다.
차례로 정렬되는 인덱스 열의 **데이터는 서로 중복되지 않고 유일성을 유지**한다.
```sql
ALTER TABLE 학생
ADD UNIQUE INDEX 연락처_인덱스(연락처);
```
> 기본 키와 고유 인덱스의 차이점
**기본 키와 고유 인덱스** 모두 **데이터의 유일성을 보장해야 하는 특성**과 **효율적인 데이터 접근을 위한 인덱스의 수단으로 사용**
다만, **기본 키에는 NULL을 입력할 수 없지만** **고유 인덱스**에는 **얼마든지 입력할 수 있다**는 차이점이 존재.
>
### 비고유 인덱스 Non-unique Index
비고유 인덱스는 고유 인덱스에서 데이터의 유일한 속성만 제외한 키이다.
인덱스가 재정렬되더라도 **인덱스 열의 중복 체크를 거치지 않는다.**
```sql
ALTER TABLE 학생
ADD INDEX 이름_인덱스 (이름);
```
### 뷰 View
뷰 == 가상 테이블
**제한된 정보만을 제공**할 수 있다.
**시스템을 안전하게 운영**하고 **개발**할 수 있는 환경을 제공하는 만큼 **보안성 측면**에서 뷰의 **가치가 부각**될 수 있다.
> 뷰를 사용하는 이유
**일부 데이터**에 대해서만 데이터를 **공개**하고, 노출에 민감한 **데이터**에 대해서는 **제약을 설정**할 수 있는 **보안성** 때문.
또한, 여러 개의 테이블을 병합해서 활용할 대는 **성능**을 고려한 **최적화된 뷰를 생성함**으로써 **일관된 성능 제공**
>
## 2.2.1 서브쿼리 위치에 따른 SQL 용어
서브쿼리(subquery)란 쿼리 안의 보조쿼리를 가리키는 용어이다.
가장 바깥쪽의 `SELECT` 문인 메인 쿼리(main query)를 기준으로 안쪽에 작성된 `SELECT` 에 따라 불리는 이름이 다르다.
```sql
SELECT (SELECT ... FROM ...) -- SELECT 절: 스칼라 서브쿼리 (scalar subquery)
FROM (SELECT ... FROM ...) -- FROM 절: 인라인 뷰(inline view)
WHERE COLUMN_NAME IN (SELECT ... FROM ...) -- WHERE 절: 중첩 서브쿼리(nested subquery)
```
### 스칼라 서브쿼리
`**SELECT` 절에서 사용되는 서브쿼리**를 **스칼라 서브쿼리**라고 한다.
스칼라 서브쿼리는 `SELECT` 절 뿐만 아니라 `FROM` , `WHERE` 절 안에서도 사용이 가능하다.
**스칼라 서브쿼리의 결괏값은 1행 1열의 구조로 출력되어야 한다.**
만약 스칼라 서브쿼리의 결과값이 2개 이상 나온다면 에러가 발생할 것이다.
### 인라인 뷰
`**FROM` 절에서 사용되는 서브쿼리**를 **인라인 뷰**라고 한다.
`**FROM` 절 내부**에서 **일시적으로 뷰를 생성하는 방식**이므로 인라인 뷰라고 불린다.
**인라인 뷰의 결과**는 내부적으로 **메모리 또는 디스크에** **임시 테이블을 생성하여 활용**한다.
### 중첩 서브쿼리
`**WHERE` 절에서 사용되는 서브쿼리를 중첩 서브쿼리**라고 한다.
중첩 서브쿼리는 보통 비교연산자(=, <, >, ≤, ≥, <>, ≠)를 비롯해 `IN`, `EXISTS`, `NOT EXIST` 문에 많이 사용한다.
## 2.2.2 메인쿼리와의 관계성에 따른 SQL 용어
### 비상관 서브쿼리 Non Correlated Subquery
비상관 서브쿼리는 **메인쿼리와 서브쿼리 간에 관계성이 없음**을 말한다.
즉, **서브쿼리가 독자적으로 실행된 뒤 메인쿼리에게 그 결과를 던져주는 형태**인 것이다.
```sql
SELECT ...
FROM 학생
WHERE ... IN (
SELECT ... FROM 지도교수 -- 비상관 쿼리 -> 메인 쿼리와 연관성 없음
)
-- 실행순서: 서브쿼리 -> 메인쿼리
```
### 상관 서브 쿼리 Correlated Subquery
상관 서브쿼리는 **메인쿼리와 서브쿼리 간에 관계성이 있음**을 의미한다.
**서브쿼리가 실행되려면 메인쿼리의 값을 받아야 함**으로 서로 관계가 깊게 유지된다.
```sql
SELECT ...
FROM 학생
WHERE ... IN (
SELECT ... FROM 지도교수 WHERE 학생.학번 = ... -- 상관 쿼리 -> 메인 쿼리에 영향을 받음 학생.학번
)
-- 실행 순서: 메인쿼리 -> 서브쿼리 -> 메인쿼리
```
### 두둥 옵티마이저 등장!
우리는 의도하는 결과를 얻기위해 위와 같이 서브쿼리들을 사용해서 쿼리를 작성할 것이다.
그러나 우리가 작성한 쿼리는 결국 옵티마이저에 의해 실행계획이 결정된다.
그렇기 때문에 **DB 버전 혹은 옵티마이저에 따라** **서브쿼리가 제거**되고 **하나의 메인쿼리로 통합**되는 **뷰 병합(View Merging), 즉 SQL 재작성(rewrite)이 작동할 수 있다.**
## 2.2.3 반환 결과에 따른 SQL 용어
### 단일행 서브쿼리 Single-Row Subquery
**서브쿼리 결과**가 **1건의 행으로 반환**되는 쿼리이다.
**주로 스칼라 서브쿼리**라고 생각하면 된다.
### 다중행 서브쿼리 Multiple-Row Subquery
**서브쿼리 결과가 여러 건의 행으로 반환**되는 쿼리이다.
```sql
SELECT ...
FROM ...
WHERE 학번 IN (
SELECT MAX(학번) FROM 학생 GROUP BY 전공코드
) -- 다중행 서브쿼리
```
### 다중열 서브쿼리 Multiple-Column Subquery
**서브쿼리 결과가 여러 개의 열과 행**으로 반환되는 쿼리이다.
```sql
SELECT ...
FROM ...
WHERE (이름, 전공코드) IN (
SELECT 이름, 전공코드 FROM 학생 WHERE 이름 LIKE '김%'
) -- 다중열 서브쿼리
```
## 2.2.4 조인 연산방식 용어
다수의 테이블에 **흩어져 있는 데이터를 결합할 때** **조인(Join) 방식 사용**
### 내부 조인 Inner Join
**양쪽에 모두 존재하는 데이터**만 반환 (교집합)
```sql
-- 명시적 조인
SELECT 학생.학번, 학생.이름, 지도교수.교수명
FROM 학생
INNER JOIN 지도교수
ON 학생.학번 = 지도교수.학번
-- 암시적 조인
SELECT 학생.학번, 학생.이름, 지도교수.교수명
FROM 학생, 지도교수
WHERE 학생.학번 = 지도교수.학번
```
### 왼쪽 외부 조인 Left Outer Join
왼쪽 테이블을 기준으로 **조건과 일치하지 않더라도** 왼**쪽 테이블의 결과는 최종 결과에 포함**된다.
```sql
SELECT 학생.학번, 학생.이름, 지도교수.교수명
FROM 학생
LEFT OUTER JOIN 또는 LEFT JOIN 지도교수
ON 학생.학번 = 지도교수.학번
```
### 오른쪽 외부 조인 Right Outer Join
**조인 조건과 일치하지 않더라도** **오른쪽 테이블의 결과는 최종 결과에 포함**된다.
```sql
SELECT 지도교수.학번, 학생.이름, 지도교수.교수명
FROM 학생
RIGHT OUTER JOIN 또는 RIGHT JOIN 지도교수
ON 학생.학번 = 지도교수.학번
```
> LEFT JOIN vs RIGTH JOIN
사람의 인지적 특성상 보통 왼쪽 → 오른쪽을 정방향으로 인식.
따라서, LEFT JOIN을 주로 사용하게 됨.
일관된 조인문을 사용함으로써 유지보수나 관리편의성 측면에서 이점을 얻자
>
> FULL OUTER JOIN은?
FULL OUTER JOIN의 경우 LEFT JOIN 과 RIGTH JOIN이 통합된 방식으로 MySQL과 MariaDB에서는 지원하지 않는다고 한다.
>
### 교차 조인 Cross Join
**교차 조인**은 **데카르트 곱(cartesian product) 곱집합** 개념, **조인에 참여**하는 테이블에서 **발생할 수 있는 모든 조합을 찾아내어 반환.**
```sql
-- 명시적 조인
SELECT 학생.학번, 학생.이름, 지도교수.학번, 지도교수.교수명
FROM 학생
CROSS JOIN 지도교수
-- 암시적 조인
SELECT 학생.학번, 학생.이름, 지도교수.학번, 지도교수.교수명
FROM 학생, 지도교수
-- 암시적 조인의 경우 WHERE 절의 조인 조건문이나
-- JOIN 키워드를 명시하지 않고 작성하면 CROSS JOIN이 된다.
```
### 자연 조인 Natural Join
2개 테이블에 **동일한 Column 이름이 있을 때 조건을 따로 작성하지 않아도 조인**이 되는 방식.
```sql
SELECT 학생.*, 지도교수.*
FROM 학생
NATURAL JOIN 지도교수
```
단, 동일한 Column 이름이 존재하지 않을경우 Cross Join발생.
### 그럼 무슨 조인 사용할까?
명시적 `**INNER JOIN**` 혹은 `**LEFT OUTER JOIN**` 무조건이다.
가독성과 유지보수성이 넘사.
## 2.2.5 조인 알고리즘 용어
### 드라이빙 테이블(Driving table)과 드리븐 테이블(Driven table)
드라이빙 테이블 == 먼저 접근하는 테이블
드리븐 테이블 == 뒤 늦게 접근하는 테이블
```sql
SELECT 학생.학번, 학생.이름, 비상연락망.관계, 비상연락망.연락처
FROM 학생
JOIN 비상연락망
ON 학생.학번 = 비상연락망. 학번
WHERE 학생.학번 IN(1, 100)
-- 드라이빙 테이블 == 학생
-- 드리븐 테이블 == 비상연락망
```
가능하면 **적은 결과가 반환될 것**으로 **예상되는 테이블**을 **드라이빙 테이블**로 선정.
**조인 조건절의** **열**이 **인덱스로 설정**되도록 구성.
### 중첩 루프 조인 Nested Loop Join, NL Join
**중첩 루프 조인**은 **드라이빙 테이블**의 **데이터 1건당** **드리븐 테이블**을 **반복**해 검색하며 최종적으로 양쪽 테이블에 공통된 데이터를 출력한다. (드라이빙 테이블 N * 드리븐 테이블 M)
```sql
SELECT ...
FROM 학생
JOIN 비상연락망
ON 학생.학번 = 비상연락망.학번
WHERE 학생.학번 IN(1, 100)
--
-- 드라이빙 테이블 결과는 1과 100 2개
-- 드리븐 테이블 비상연락망의 테이블이 만약 1000건이라고 하면
-- 조건에 맞는 데이터를 가져오기 위해 최악 2000건의 데이터 접근이 일어나게 됨.
-- 만약, 조인 조건절이 모두 인덱스가 걸려있다면 == 학생 테이블 학번 컬럼 인덱스, 비상연락망 학번 컬럼 인덱스
-- 드리븐 테이블 비상연락망 테이블에서 조건에 맞는 인덱스만 접근.
-- 데이터 접근 횟수 감소.
```
인덱스는 **인덱스로 정의된 열 기준으로 순차 정렬**되지만, 해당 인덱스가 **비고유 인덱스일 경우**
인덱스를 이용해 테이블의 데이터를 찾아가는 과정에서 **임의 접근 방식인 랜덤 액세스(Random Access)가 발생**한다.
따라서, 랜덤 액세스를 줄일 수 있도록 **데이터의 액세스 범위를 좁히는 방향**으로 **인덱스를 설계**하고 **조건절을 작성**해야 한다.
**기본 키**는 **클러스터형 인덱스**이므로 기본 키의 순서대로 테이블의 데이터가 적재되어 있어 **조회 효율이 매우 높다.**
### 블록 중첩 루프 조인 Block Nested Loop Join, BNL Join
**인덱스가 걸려있지 않는 상황**의 **중첩 루프 조인의 성능을 개선하기 위해** 등장.
1. **조인 버퍼(Join Buffer)**에 **드라이빙 테이블의 결과**를 조인 버퍼가 가득 찰때까지 **적재.**
2. **조인 버퍼와 드리븐 테이블 데이터를 조인**하는 식으로 **반복**.
해당 과정은 **드리븐 테이블의 풀 스캔을 줄이는 게 목적**이다.
드라이빙 테이블 Full Scan → 드리븐 테이블 Full Scan 반복을 최소화 시킬 수 있다.
### 배치 키 엑세스 조인 Batched Key Access Join(BKA 조인)
**중첩 루프 조인 방식의 인덱스의 랜덤 엑세스의 단점을 해결하고자,**
**접근할 데이터를 미리 예상하고 가져오는**데 착안된 조인 알고리즘.
블록 중첩 루프 조인 방식의 **조인 버퍼 개념을 사용**하고, **드리븐 테이블**의 **데이터를 예측**하고 **정렬된 상태**로 담는 **랜덤 버퍼의 개념이 추가**되었다.
이때, **드리븐 테이블의 데이터를 예측하고 정렬된 상태로 버퍼에 적재하는 기능**을 **다중 범위 읽기(Multi Range Read, MRR)**라고 한다.
**미리 예측, 정렬된 데이터를 액세스** 하기 때문에 랜덤 액세스가 아닌 **시퀀셜 액세스가 일어나게 된다.**
### 해시 조인 Hash Join
MySQL 8.0.18 버전 부터 지원되는 방식.(상용 DMBS 오라클 같은 애들은 이미 지원하고 있었다고 한다.)
블록 중첩 루프 조인과 배치 키 액세스 조인의 한계를 탈피하기 위함.
**조인에 참여하는 각 테이블의 데이터**를 내부적으로 **해시값으로 만들어 내부 조인을 수행**한다.
**내부 조인 수행 결과**는 **조인 버퍼에 저장**되므로 **조인열의 인덱스를 필수로 요구하지 않아도 된다.**
## 2.3.1 기초 용어 - 오브젝트 스캔 유형
### 테이블 풀 스캔 Table Full Scan
테이블 풀 스캔은 **인덱스를 거치지 않고** **테이블로 바로 직행**하여 **처음부터 끝까지 데이터를 훑어보는 방식.**
테이블 풀 스캔은 **인덱스 없이 사용하는 유일한 방식**. **성능 측면에서는 부정적**.
### 인덱스 범위 스캔 Index Range Scan
인덱스 범위 스캔은 **인덱스를 범위 기준으로 스캔**한 뒤 **스캔 결과를 토대로** **테이블의 데이터를 찾아가는 방식.**
좁**은 범위를 스캔**할 때 **매우 효율적**, 넓은 범위의 경우 비효율적인 방식
SQL의 `BETWEEN ~ AND`, `<`, `>`, `LIKE` 구문 등에 사용됨.
### 인덱스 풀 스캔 Index Full Scan
인덱스 풀 스캔은 **인덱스를 처음부터 끝까지 스캔**하는 방식.
단, **테이블에 접근하지 않고** **인덱스로 구성된 열 정보만 요구하는 SQL 문에서 인덱스 풀 스캔이 수행됨.**
테이블 풀 스캔보다는 성능 좋음.
그러나, 최대한 검색 범위를 줄이는 방향으로 SQL 튜닝 권장.
### 인덱스 고유 스캔 Index Unique Scan
인덱스 고유 스캔은 **기본 키나 고유 인덱스로 테이블에 접근하는 방식.**
인덱스를 사용하는 스캔 방식 중 **가장 효율적인 스캔 방법.**
`**WHERE 기본 키 혹은 고유 인덱스 컬럼 = 조건**` 으로 되어 있을 경우 활용됨.
### 인덱스 루스 스캔 Index Loose Scan
인덱스 루스 스캔은 **인덱스의 필요한 부분들만 골라 스캔하는 방식.**
**필요**한 데이터와 불필요한 **데이터를 구분**한 뒤 불필요한 인덱스 키는 무시.
`GROUP BY` , `MAX()`, `MIN()` 함수의 경우 작동.
### 인덱스 병합 스캔 Index Merge Scan
인덱스 병합 스캔은 **테이블 내에 생성된 인덱스들을 통합해서 스캔하는 방식.**
**옵티마이저**가 **서로 다른 인덱스**를 가져와 **결합(Union)과 교차(Intersection) 방식**을 통해 서로 다른 인덱스를 **각각 실행**하며 원하는 데이터를 찾음.
개별 인덱스를 각각 수행하므로 **인덱스에 접근하는 시간이 몇 배로 걸림**.
따라서, 별개의 인덱스를 **하나의 인덱스로 통합** 혹은 SQL문 자체를 **독립된 하나의 인덱스만 사용**하도록 **변경**
## 2.3.1 기초 용어 - 디스크 접근 방식
### 시퀀셜 액세스 Sequential Access
인접한 페이지를 차례대로 읽는 **순차 접근** 방식, 테이블 풀 스캔에서 활용됨.
### 랜덤 액세스 Random Access
물리적으로 떨어진 페이지들에 **임의 접근** 방식.
물리적으로 떨어진 페이지에 접근하기 때문에 **디스크 헤더가 움직이는 비용이 보다 큼.**
**접근 범위를 줄이고** **인덱스를 활용할 수 있도록 튜닝**해야 함.
## 2.3.1 기초 용어 - 조건 유형
### 엑세스 조건 Access Condition
**디스크에 있는 데이터에 어떻게 접근할 것인지**를 다루는 **SQL 튜닝의 핵심**.
**옵티마이저**는 `**WHERE` 절의 특정 조건문을 이용**해 **소량의 데이터를 가져**오고, **인덱스**를 통해 **시간 낭비를 줄이는 조건절을 선택**하여, 스토리지 엔진에 접근하고 MySQL 엔진으로 데이터를 가져온다.
```sql
SELECT *
FROM TAB
WHERE ID = 1
AND CODE ='A';
-- ID 컬럼 인덱스 O, CODE 컬럼 인덱스 X
```
옵티마이저는 위와 같은 경우 인덱스가 존재하는 `ID` 컬럼을 통해 디스크에 접근한다.
이게 엑세스 조건이다.
### 필터 조건 Filter Condition
엑세스 조건을 이용해 MySQL 엔진으로 **가져온 데이터**를 기준으로 추가로 **불필요한 데이터를 제거**하거나 가공하는 **조건.**
필터 조건으로 **필터링되어 제거된 데이터가 다수 존재 할수록 효율이 좋지 않다.**
**스토리지에서 MySQL 엔진**으로 넘기는 데이터의 **오버헤드가 발생**하기 때문.
**제거될 데이터**라면 **스토리지 엔진에 접근하는 과정에서 같이 제외**되는 편이 **효율적**
```sql
SELECT *
FROM TAB
WHERE ID = 1
AND CODE ='A'; -- CODE = 'A' 이게 필터 조건임.
```
## 2.3.2 응용 용어
### 선택도 Selectivity
테이블의 **특정 열을 기준**으로 해당 열의 **조건절에 따라** **선택되는 데이터 비율**
**낮은 선택도를 가지는 열**은 **인덱스 생성**할 때 **주요 고려 대상**
```sql
선택도 = 선택한 데이터 건수 / 전체 데이터 건수
변형된 선택도 = 1 / DISTINCT(COUNT COLUMN_NAME) -- 보통 중복이 제거된 데이터의 건수를 활용함.
```
```sql
-- 예시 학번
-- 총 100명의 학생 중 학번을 통해 학생을 찾는 경우
학번 열의 선택도 = 1 / 100 = 0.01 -- 낮은 선택도
-- 예시 성별
-- 총 100명의 학생 중 성별을 통해 학생을 찾는 경우
성별 열의 선택도 = 50 / 100 = 0.5 -- 높은 선택도
```
### 카디널리티 Cardinality
사전적 정의는 하나의 데이터 유형으로 정의되는 데이터 행의 개수.
현업에서는 **전체 행에 대한** **특정 열**의 **중복 수치**를 나타내는 **지표**로 사용.
**중복**되는 값이 **적**다면 **카디널리티 높음**
**중복**되는 값이 **많**으면 **카디널리티 낮음**
```sql
카디널리티 = 전체 데이터 건수 * 선택도
-- 예시
-- 주민등록번호: 카디널리티 높음
-- 이름: 카디널리티 중간
-- 성별: 카디너리티 낮음
```
### 힌트 Hint
우리가 데이터베이스에 **데이터를 빨리 찾을 수 있도록** **추가 정보를 전달하는 것**
```sql
-- 주석처럼 힌트 명시하는 법
SELECT 학번, 전공코드
FROM 학생 /*! USE INDEX (학생_IDX01) */
WHERE 이름 = '유재석';
-- 쿼리의 일부로 작성
SELECT 학번, 전공코드
FROM 학생 USE INDEX (학생_IDX01)
WHERE 이름 = '유재석';
```
단, **옵티마이저가 우리의 힌트를 무조건 참고하는건 아니다.** 비효율적이라고 생각되면 얄짤 없다. (과연 내가 옵티마이저보다 뛰어날까?)
**힌트를 사용할 경우 별도의 관리가 필요하다.**
어느 **특정 인덱스**를 이용해 **힌트를 관리**하다가 그 **인덱스를 삭제**하게 된다면 **에러를 발생**시키고 **서비스 장애**가 된다.
### 콜레이션 Collation
**특정 문자셋**으로 데이터베이스에 저장된 값을 **비교**하거나 **정렬**하는 **규칙.**
```sql
-- 예시
utf8_bin: A > B > a > b
utf8_general_ci: A > a > B > b
```
> 그럼 캐릭터 셋(Character Set)이랑 뭐가 다름?
캐릭터 셋 == 데이터를 어떻게 저장 할 것인지에 대한 규칙
콜레이션 == 데이터를 어떻게 비교할 것인지에 대한 규칙
Discussed in https://github.com/FeGwan-Training/FeGwan/discussions/69