결합
기능적 관점으로 구분하는 결합의 종류
크로스 결합 - 모든 결합의 모체
- 데카르트 곱이라고 불리는 연산으로, 2개 테이블의 레코드에서 가능한 모든 조합을 구하는 연산
- 크로스 결합이 실무에서 사용되지 않는 이유
- 이러한 결과가 필요한 경우가 없음
- 비용이 매우 많이 드는 연산
- 실수로 사용한 크로스 결합
SELECT * FROM Employees, Departments;
- 내부는 ‘데카르트 곱의 부분 집합’이라는 의미
- 기능적으로 상관 서브쿼리를 사용해 대체 가능한 경우가 많음
SELECT E.emp_id, E.emp_name, E.dept_id, (SELECT D.dept_name FROM Departments D WHERE E.dept_id = D.dept_id) AS dept_name FROM Employees E;
- 내부 결합과 상관 서브쿼리 중 내부 결합을 사용하는 것이 좋음
- 내부 결합과 외부 결합은 배타적인 연산
- 외부 결합은 ‘데카르트 곱의 부분 집합’이 아니라는 읨
- 외부 결합의 종류
- 왼쪽 외부 결합
- 오른쪽 외부 결합
- 완전 외부 결합
- 마스터 테이블 쪽에만 존재하는 키가 있을 때 해당 키를 제거하지 않고 결과에 남김
내부 결합과 외부 결합의 차이
- 외부 결합은 마스터 테이블의 정보를 모두 보존하기 위해 NULL을 생성
- 크로스 결합과 내부 결합은 NULL을 생성하지 않음
자기 결합 - ‘자기’란 누구일까?
- 자기 자신과 결합하는 연산으로 같은 테이블 (또는 같은 뷰)을 사용해 결합
- 같은 테이블에 별칭을 붙여 마치 다른 테이블인 것처럼 취급
결합 알고리즘과 성능
- 옵티마이저가 어떤 결합 알고리즘을 선택할지 여부는 데이터 크기 또는 결합 키의 분산에 의존
Nested Loops
- 결합 대상 테이블(Table_A, driving table or outer table)에서 레코드를 하나씩 반복해가며 스캔
- Driving table의 레코드 하나마다 내부 테이블(Table_B, Inner table)의 레코드를 하나씩 스캔해서 결합 조건에 맞으면 리턴
- 이러한 작동을 Driving Table의 모든 레코드에 반복
- 특징
- 접근하는 레코드 수는 결합 대상인 두 테이블 레코드 수의 곱
- 한 번의 단계에서 처리하는 레코드 수가 적으므로 Hash 또는 Sort Merge에 비해 메모리 소비가 적음
- 모든 DBMS에서 지원
- 구동 테이블이 작을수록 Nested Loops의 성능이 좋음
- 구동 테이블의 중요성
- 내부 테이블의 결합 키 필드에 인덱스가 존재할 경우 내부 테이블의 반복을 줄일 수 있음
- 구동 테이블이 작고 내부 테이블의 결합 키에 인덱스가 존재하는 것이 SQL 튜닝의 기본
- Nested Loops의 단점
- 결합 과정
- 작은 테이블을 스캔하고 결합 키에 해시함수를 적용해서 해시값으로 반환
- 큰 테이블을 스캔하고 결합 키가 해시값에 존재하는지 확인
- 특징
- 결합 테이블로부터 해시 테이블을 만들어서 활용하므로, Nested Loops에 비해 메모리를 크게 소모
- 메모리가 부족하면 저장소를 사용하므로 지연 발생
- 출력되는 해시값은 입력값의 순서를 알지 못하므로 등치 결합에만 사용할 수 있음
- Hash가 유용한 경우
- Nested Loops에서 적절한 구동 테이블(상대적으로 충분히 작은 테이블)이 존재하지 않는 경우
- 구동 테이블로 사용할만한 작은 테이블은 있지만, 내부 테이블에서 히트되는 레코드 수가 너무 많은 경우
- Nested Loops의 내부 테이블에 인덱스가 존재하지 않는 (또는 여러 가지 사정에 의해 인덱스를 추가할 수 없는) 경우
- 주의할 사항
결합 대상 테이블들을 각각 결합 키로 정렬하고 일치하는 결합 키를 찾으면 결합
- 특징
- 대상 테이블을 모두 정렬하므로 Nested Loops보다 많은 메모리를 소비
- 동치 결합 뿐만 아니라 부등호를 사용한 결합에도 사용할 수 있음
- 테이블이 결합 키로 정렬되어 있다면 정렬을 생략할 수 있지만 이는 구현 의존적
- 한 쪽 테이블을 모두 스캔한 시점에 결합을 완료함
- Sort Merge가 유용한 경우
- 삼각 결합에서 결합 조건이 존재하지 않는 두 테이블을 먼저 결합할 경우 크로스 결합이 발생
- 비교적 큰 테이블끼리 크로스 결합을 할 경우 성능 문제 발생
- 크로스 결합이 발생하는 두 테이블의 결합 조건을 설정할 수 있고, 그 결합 조건이 결과에 아무런 영향도 주지 않을 경우 크로스 결합 회피 가능
결합이 느리다면
상황에 따른 최적의 결합 알고리즘
|이름 |장점 |단점 | |–|–|–| |Nested Loops |’작은 구동 테이블’ + ‘내부 테이블의 인덱스’라는 조건이 있다면 굉장히 빠름
메모리 또는 디스크 소비가 적으므로 OLTP에 적합
비동기 결합에서도 사용 가능 |대규모 테이블들의 결합에는 부적합
내부 테이블의 인덱스가 사용되지 않거나, 내부 테이블의 선택률이 높으면 느리다 | |Hash |대규모 테이블들을 결합할 때 적합 |메모리 소비량이 큰 OLTP에는 부적합
메모리 부족이 일어나면 TEMP 탈락 발생
등가 결합에서만 사용 가능 | |Sort Merge |대규모 테이블들을 결합할 때 적합
비등가 결합에서도 사용 가능 |메모리 소비량이 큰 OLTP에는 부적합
메모리 부족이 일어나면 TEMP 탈락 발생
데이터가 정렬되어 있지 않다면 비효율적 | - 결합 대상 레코드 수에 따른 최적의 결합 알고리즘
- 소규모 - 소규모: 어떤 알고리즘을 사용해도 성능 차이가 크지 않음
- 소규모 - 대규모: 대규모 테이블의 결합 키에 인덱스를 만들어 Nested Loops를 사용. 하지만 내부 테이블의 결합 대상 레코드가 너무 많다면 구동 테이블과 내부 테이블을 바꾸거나 Hash 사용
- 대규모 - 대규모: Hash 사용. 처음부터 결합 키로 정렬이 되어 있는 상태라면 Sort Merge 사용
실행 계획 제어
- RDB에서는 원칙적으로 통계 정보를 바탕으로 옵티마이저가 실행 계획을 자동으로 수립
- 실행 계획을 사용자가 제어할 때의 리스크
- 데이터 양의 증가 등에 따라 통계 정보가 변했을 때 일정한 역치를 넘으면 ‘옵티마이저의 실패’가 발생할 수 있음
- SQL 성능의 변동 위험을 줄이려면 되도록 결합을 피해야 함