Search
Duplicate
4️⃣

SELECT

: 웹 서비스 같이 일반적인 온라인 트랜잭션 처리 환경의 데이터베이스에서는 INSERT나 UPDATE 같은 작업은 거의 레코드 단위로 발생하므로 성능상 문제가 되는 경우가 별로 없음
: 하지만 SELECT는 여러 개의 테이블로부터 데이터를 조합해서 빠르게 가져와야 하기 때문에 여러 개의 테이블을 어떻게 읽을 것인가에 주의를 기울여야 함

1. SELECT 절의 처리 순서

: SELECT 문장이라고 하면 SQL 전체를 의미
SELECT s.emp_no, COUNT(DISTINCT e.first_name) AS cnt FROM salaries s INNER JOIN employees e ON e.emp_no=s.emp_no WHERE s.emp_no IN (100001, 100002) GROUP BY s.emp_no HAVING AVG(s.salary) > 1000 ORDER BY AVG(s.salary) LIMIT 10;
SQL
복사
SELECT 절: SELECT s.emp_no, COUNT(DISTINCT e.first_name) AS cnt
FROM 절: FROM salaries s INNER JOIN employees e ON e.emp_no=s.emp_no
WHERE 절 : WHERE s.emp_no IN (100001, 100002)
GROUP BY 절 : GROUP BY s.emp_no
HAVING 절 : HAVING AVG(s.salary) > 1000
ORDER BY 절 : ORDER BY AVG(s.salary)
LIMIT 절 : LIMIT 10
FROM → GROUP BY → DISTINCT → HAVING → ORDER BY → LIMIT
WHERE → ORDER BY → JOIN → LIMIT

2. WHERE 절과 GROUP BY 절, ORDER BY 절의 인덱스 사용

: GROUP BY나 ORDER BY도 인덱스를 이용해 빠르게 처리할 수 있다는 점을 언급했으니 어떤 요건을 갖췄을 때 사용할 수 있는지
1.
인덱스를 사용하기 위한 기본 규칙
: WHERE 절이나 ORDER BY 또는 GROUP BY가 인덱스를 사용하려면 기본적으로 인덱스된 칼럼의 값 자체를 변환하지 않고 그대로 사용해야 함
2.
WHERE 절의 인덱스 사용
: 작업 범위 결정 조건과 체크 조건의 두 가지 방식
작업 범위 결정 조건
: WHERE 절에서 동등 비교 조건이나 IN으로 구성된 조건에 사용된 칼럼들이 인덱스의 칼럼 구성과 좌측에서부터 비교했을 때 얼마나 일치하는 가에 따라 달라짐
체크 조건
: 인덱스의 특정 값이 가리키는 범위로 제한
3.
GROUP BY 절의 인덱스 사용
: GROUP BY가 사용되면 인덱스의 사용 여부는 GROUP BY 절에 명시된 칼럼의 순서가 인덱스를 구성하는 칼럼의 순서와 같으면 이용할 수 있음
4.
ORDER BY 절의 인덱스 사용
: GROUP BY 절과 ORDER BY 절의 인덱스 사용은 매우 유사
5.
WHERE 조건과 ORDER BY(또는 GROUP BY) 절의 인덱스 사용
: 일반적으로 다음 3가지 방법 중 하나로 인덱스를 사용
WHERE 절과 ORDER BY 절이 동시에 같은 인덱스를 이용
: WHERE 절의 비교 조건에서 사용하는 칼럼과 ORDER BY 절의 정렬 대상 칼럼이 모두 하나의 인덱스에 연속해서 포함되어있을 때 사용 가능, 다른 2가지 방법보다 빠름
WHERE 절만 인덱스를 이용
: ORDER BY 절은 인덱스를 이용한 정렬이 불가능하며 인덱스를 통해 검색된 결과를 별도의 정렬 처리 과정(Using filesort)를 거쳐 정렬을 수행
: 주로 WHERE 절의 조건에 일치하는 레코드가 적을 때 효과적인 방법
ORDER BY 절만 인덱스를 이용
: 주로 아주 많은 레코드를 조회해서 정렬해야할 때 효과적인 방법
6.
ORDER BY 절과 GROUP BY 절의 인덱스 사용
: GROUP BY 절에 명시된 칼럼과 ORDER BY 절에 명시된 칼럼의 순서와 내용이 모두 같아야 함
7.
WHERE 조건과 ORDER BY 절, GROUP BY 절의 인덱스 사용
: 다음 3개의 질문을 기본으로 해서 인덱스를 사용하는지 판단할 수 있음
1.
WHERE 절이 인덱스를 사용할 수 있는가?
2.
GROUP BY 절이 인덱스를 사용할 수 있는가?
3.
GROUP BY 절과 ORDER BY 절이 동시에 인덱스를 사용할 수 있는가?

3. WHERE 절의 비교 조건 사용 시 주의사항

: WHERE 절에 사용되는 비교 조건의 표현식은 상당히 중요함, 쿼리가 최적으로 실행되려면 적합한 인덱스와 함께 비교 조건의 표현식을 적절하게 사용해야 함
1.
NULL 비교
: MySQL에서는 다른 DBMS와는 달리 NULL 값이 포함된 레코드도 인덱스로 관리됨, 이렇듯 NULL 값이 존재하는 인덱스를 효과적으로 이용하려면 IS NULL 등을 잘 활용
2.
문자열이나 숫자 비교
: 문자열 칼럼이나 숫자 칼럼을 비교할 때는 반드시 그 타입에 적절한 상숫값을 사용할 것을 권장, 불필요한 형변환을 방지하여 성능을 향상시키고자 함
3.
날짜 비교
: 실수를 많이할 수 있는 부분이므로 적절히 잘 비교하는 것이 중요
1.
DATE 또는 DATETIME과 문자열 비교
: DATE 또는 DATETIME 타입의 값과 문자열을 비교할 때는 문자열 값을 자동으로 DATETIME으로 변환하여 비교를 수행, 가능하면 칼럼의 타입을 바꾸지 않고 상수를
변경하는 형태로 사용하는 것이 좋음
2.
DATE와 DATETIME의 비교
: DATETIME 값에서 시간 부분만 떼어 버리고 비교하려면 DATE로 형변환 해주면 됨, 또한 그냥 비교해도 자동으로 변환해서 비교를 수행
3.
DATETIME과 TIMESTAMP의 비교
: DATE나 DATETIME 타입의 값과 TIMESTAMP의 값을 별도로 타입 변환 없이 비교하면 문제없이 작동하고 실행계획도 문제가 없어뵈나 그렇지 않다.
: UNIX_TIMESTAMP 함수의 결괏값은 MySQL 내부적으로는 단순 숫자 값에 불과하므로 비교를 위한 형변환이 발생함
4.
Short-Circuit Evaluation
: 쿼리의 조건 순서에 따라 데이터의 영향을 받는다는 것..으로 이해했음 일단

4. DISTINCT

: 특정 칼럼의 유니크한 값을 조회할 때 사용, 남용하는 것은 성능적인 문제도 있고 쿼리의 결과가 의도한 바와 달라질 수 있음

5. LIMIT

: 쿼리 결과에서 지정된 순서에 위치한 레코드만 가져오고자 할 때 사용

6. COUNT

: 결과 레코드의 건수를 반환하는 함수

7. JOIN

: JOIN이 어떻게 인덱스를 사용하는지에 대해 쿼리 패턴별로 진행
1.
JOIN의 순서와 인덱스
: 인덱스 레인지 스캔은 인덱스를 탐색하는 단계와 인덱스를 스캔하는 과정으로 구분할 수 있음
: 일반적으로 인덱스를 이용해서 쿼리하는 작업에서는 가져오는 레코드의 건수가 소량이기 때문에 부하가 작지만 특정 인덱스 키를 찾는 인덱스 탐색 작업은 상대적으로 높음
: 조인 작업에서 드라이빙 테이블을 읽을 때는 인덱스 탐색 작업을 단 한번만 수행하고 그 이후부터는 스캔만 실행, 하지만 드리븐 테이블에서는 인덱스 탐색 작업과
스캔 작업을 드라이빙 테이블에서 읽은 레코드 건수만큼 반복, 드라이빙 테이블과 드리븐 테이블이 1대1 매핑이 되더라도 드리븐 테이블을 읽는 것은 큰 부하를 초래
SELECT * FROM employees e, dept_emp de WHERE e.emp_no=de.emp_no;
SQL
복사
: 위와 같은 쿼리에서 e, de 각각의 테이블의 emp_no에 인덱스가 걸려있을 때와 걸려있지 않을 때 조인 순서의 차이
두 칼럼 모두 각각 인덱스가 있는 경우
: 어느 테이블을 드라이빙으로 선택하든 인덱스를 이용해 드라이븐 테이블의 빠른 처리가 가능, 이때 드라이빙 테이블의 결정은 테이블의 통계정보를 가지고 진행
하나에만 있는 경우
: 인덱스가 걸려있지 않는 테이블의 레코드 건수만큼 나머지 테이블을 풀스캔해야하므로 인덱스가 걸려있지 않은 테이블을 드라이빙 테이블로 선택
둘 다 없는 경우
: 답도 없는 경우, 뭘 선택해도 풀 스캔이 발생하므로 통계정보를 가지고 처리할듯?
2.
JOIN 칼럼의 데이터 타입
: 조인 칼럼 간의 비교에서 각 칼럼의 데이터 타입이 일치하지 않으면 인덱스를 효율적으로 사용할 수 없다.
: 이 경우, 인덱스를 사용할 수 없으므로 풀 테이블 스캔을 해야함, 이때 옵티마이저가 조금이라도 빠르게 처리하려고 조인 버퍼를 사용해서 처리
3.
OUTER JOIN의 성능과 주의사항
: 이너 조인은 조인 대상 테이블에 모두 존재하는 레코드만 결과 집합으로 반환
: 아우터 조인을 처리할 때, 옵티마이저는 아우터 조인의 대상이 되는 테이블을 드라이빙 테이블로 설정할 수 없음, 때문에 아우터 조인을 사용할 경우, 가장 원본 테이블의
인덱싱 상태가 중요
4.
JOIN과 외래키
: 외래키는 JOIN과 아무 연관성이 없음
5.
지연된 조인(Delayed Join)
: 조인을 수행하기 전에 미리 GROUP BY, ORDER BY를 실행한 임시 테이블을 만들어 조인을 수행하는 것
: LIMIT과 함께 사용할 수록 효과가 좋음, 메모리에 적재해야할 레코드 건수가 줄어드므로
6.
래터럴 조인
: 특정 그룹별로 서브쿼리를 실행해서 그 결과와 조인하는 것이 가능하게 만들어주는 조인
7.
실행 계획으로 인한 정렬 흐트러짐
: 해시 조인이 8.0버전부터 추가, 해시 조인을 사용한 경우
: 네스티드 루프 방식으로 조인이 처리되면 드라이빙 테이블을 읽은 순서대로 결과가 조회되는 것과는 달리 해시 조인을 이용한 경우 정렬되어있지 않고 순환하는 결과

8. GROUP BY

: GROUP BY는 특정 칼럼의 값으로 레코드를 그루핑하고 그룹별로 집계된 결과를 하나의 레코드로 조회할 때 사용
1.
WITH ROLLUP
: GROUP BY가 사용된 쿼리에는 그루핑된 그룹별로 소계를 가져올 수 있는 롭업 기능을 사용할 수 있음
: ROLLUP으로 출력되는 소계는 단순히 최종합만 가져오는 것이 아닌 GROUP BY에 사용된 칼럼의 개수에 따라 소계의 레벨이 달라짐, MySQL의 GROUP BY … ROLLUP 쿼리는
엑셀의 피벗 테이블과 유사한 기능을 제공
SELECT dept_no, COUNT(*) FROM dept_emp GROUP BY dept_no WITH ROLLUP
SQL
복사
2.
레코드를 칼럼으로 변환해서 조회
a.
레코드를 칼럼으로 변환
: 특정 부서 아이디를 가지고 해당 부서원의 수를 구하고 싶을 때, COUNT를 사용하면 됨, 하지만 레포팅 도구나 OLAP 도구에선 이런 결과를 반대로 만들어야 할수도 있음
: 이때는 위의 GROUP BY 쿼리 결과를 SUM(CASE WHEN …) 구문을 사용해 한 번 더 변환하면 됨, 이리하면 레코드를 칼럼으로 변경할 수 있음
b.
하나의 칼럼을 여러 칼럼으로 분리
: SUM(CASE WHEN …) 문장으로 특정 조건을 이용해 소그룹으로 나눠서 데이터를 표시할 수도 있음

9. ORDER BY

: ORDER BY 절이 사용되지 않으면 SELECT 쿼리는 어떤 순서로 정렬될까?
인덱스를 사용한 SELECT의 경우, 인덱스에 정렬된 순서대로
인덱스를 사용하지 못하고 풀 테이블 스캔을 사용하는 경우, 프라이머리 키 순서대로 가져옴
SELECT 쿼리가 임시 테이블을 거쳐 처리되면 조회되는 레코드의 순서를 예측하기 어려움
⇒ 확실하진 않ㄷ.. 참고만 할 것!!!
: ORDER BY에서 인덱스를 사용하지 못할 때는 추가 정렬 작업이 수행되며 쿼리 실행 계획에 있는 Extra 칼럼에 Using filesort가 표시
1.
ORDER BY 사용법 및 주의사항
: ORDER BY 절은 1개 또는 그 이상 여러 개의 칼럼으로 정렬을 수행할 수 있으며 정렬 순서는 칼럼별로 다르게 명시할 수 있음
: 일반적으로 정렬 대상은 칼럼명이나 표현식으로 명시하지만 SELECT되는 칼럼의 수번을 명시할 수도 있음, 즉 ORDER BY 2라고 명시하면 SELECT되는 칼럼 중에서 2번째 칼럼으로 정렬하라는 의미,
즉 2번째 칼럼으로 정렬을 수행함, 그러나 문자열 레터럴을 지시한 경우는 무시
2.
여러 방향으로 동시 정렬
: 8.0 이후 버전부터는 여러 개의 칼럼을 조합해서 정렬할 때 각 칼럼의 정렬 순서를 지정하여 인덱스를 생성할 수 있게 되었음
ALTER TABLE salaries ADD INDEX ix_salary_fromdate (salary DESC, from_date ASC);
SQL
복사
3.
함수나 표현식을 이용한 정렬
: 함수 기반의 인덱스를 지원하므로 연산의 결괏값을 기준으로 정렬하는 작업이 인덱스를 사용하도록 튜닝하면 됨

10. 서브쿼리

: 쿼리를 작성할 때 서브쿼리를 이용하면 단위 처리별로 쿼리를 독립적으로 작성할 수 있게 도와줌, 조인과 달리 여러 테이블을 섞어 두는 형태가 아니므로 가독성이 향상됨
: 서브쿼리는 여러 위치에서 사용될 수 있는데 대표적으로 SELECT 절과 FROM, WHERE 절에서 사용될 수 있으며 사용되는 위치에 따라 쿼리의 성능 영향도와 MySQL 서버의
최적화 방법은 완전히 달라짐
1.
SELECT 절에 사용된 서브쿼리
: SELECT 절에 사용된 서브쿼리는 내부적으로 임시 테이블을 만들거나 쿼리를 비효율적으로 실행하게 만들지는 않으므로 서브쿼리가 적절히 인덱스를 사용한다면 상관없음
: 일반적으로 SELECT 절에 서브쿼리를 사용하면 해당 서브쿼리는 항상 칼럼과 레코드가 하나인 결과를 반환해야 함, 그 값이 NULL이든 아니든 관계없이 레코드가 1건이 존재해야한다는 것, 즉 SELECT 절의
서브쿼리는 로우 서브쿼리를 사용할 수 없고 오로지 스칼라 서브쿼리만 사용할 수 있다.
스칼라 서브쿼리는 레코드의 칼럼이 각각 하나인 결과를 만들어내는 서브쿼리이며 로우 서브쿼리는 스칼라 서브쿼리보다 많거나 칼럼 수가 많은 결과를 만들어 냄
2.
FROM 절에 사용된 서브쿼리
: 5.7 이후 버전부터는 옵티마이저가 FROM 절의 서브쿼리를 외부 쿼리로 병합하는 최적화를 수행하도록 개선되었음
: 다만 다음과 같은 기능들이 서브쿼리에 사용되면 외부 쿼리로 병합되지 못함
집합 함수
DISTINCT
GROUP BY 또는 HAVING
LIMIT
UNION(UNION DISTINCT) 또는 UNION ALL
SELECT 절에 서브쿼리가 사용된 경우
사용자 변수 사용
3.
WHERE 절에 사용된 서브 쿼리
a.
동등 또는 크다 작다 비교
: 단일 값 비교가 아닌 튜플 비교 방식이 사용되면 서브쿼리가 먼저 처리되어 상수화되며 외부 쿼리는 인덱스를 사용하지 못하고 풀 테이블 스캔을 진행
b.
IN 비교
: 실제 조인은 아니지만 다른 테이블의 레코드를 이용해 표현식과 일치하는 지 체크하는 형태를 세미 조인이라고 함, 즉 WHERE 절에 IN 형태의 조건을 세미 조인으로 봄
c.
NOT IN 비교
: IN과 비슷한 형태지만 안티 세미 조인으로 명명, 안티 세미 조인 쿼리가 사용되면 NOT EXISTS, Materialization으로 최적화를 수행

11. CTE

: 뭔지.. 추후 작성

12. 윈도우 함수

: 윈도우 함수는 조회하는 현재 레코드를 기준으로 연관된 레코드 집합의 연산을 수행
: 집계 함수는 주어진 그룹 별로 하나의 레코드로 묶어서 출력하지만 윈도우 함수는 조건에 일치하는 레코드 건수는 변하지 않고 그대로 유지
: 일반적인 SQL 문장에서 하나의 레코드를 연산할 때 다른 레코드의 값을 참조할 수 없는데 예외적으로 GROUP BY나 집계 함수를 이용하면 다른 레코드의 칼럼값을 참조할 수 있음, 하지만 GROUP BY 또는
집계 함수를 사용하면 결과 집합의 모양이 바뀜, 그에 반해 윈도우 함수는 결과 집합을 그대로 유지하면서 하나의 레코드 연산에 다른 레코드의 칼럼값을 참조할 수 있음 → 은행 입출금 내역같은거!!
1.
쿼리 각 절의 실행 순서
: 윈도우 함수를 사용하는 쿼리의 결과에 보여지는 레코드는 FROM, WHERE, GROUP BY, HAVING 절에 의해 결정되며 이후 윈도우 함수가 실행, 그리고 SELECT, ORDER BY, LIMIT 절이 실행되어 최종 결과가
반환, 즉 윈도우 함수를 GROUP BY 칼럼으로 사용하거나 WHERE 절에 사용이 불가하다는 것
2.
윈도우 함수 기본 사용법
: 윈도우 함수의 기본 사용법은 다음과 같음 AGGREGATE_FUNC() OVER(<partition> <order>) AS window_func_column
: 윈도우 함수는 용도별로 다양한 함수들을 사용할 수 있는데 집계 함수와는 달리 함수 뒤에 OVER 절을 이용해 연산 대상을 파티션하기 위한 옵션을 명시할 수 있음