8강. UNION을 사용한 쓸데없이 긴 표현
- UNION을 이용해 조건분기하는 방식은 성능적 측면에서 굉장히 큰 단점
- 내부적으로 여러 SELECT 구문을 실행하는 실행계획
- 테이블 접근 횟수 증가 → I/O 비용 증가
- CASE를 이용한 조건분기 권장
UNION을 사용한 조건 분기와 관련된 간단한 예제
핵심
- CASE 식 사용한 조건 분기
식
을 바탕으로 하는 사고
- SQL 마스터키 =
구문
에서 식
으로 사고를 변경
- 절차지향의 IF가 생각날 때마다 ‘CASE로는 어떻게 할까’를 고민
문제
- 2001년까지는 세금 미포함, 2002년부터는 세금 포함
SELECT item_name, year, price_tax_ex AS price
FROM Items
WHERE year <= 2001
UNION ALL
SELECT item_name, year, price_tax_in AS price
FROM Items
WHERE year >= 2002;
- 문제점
- 가독성 측면
- 성능 측면
- TABLE ACCESS FULL 2회 발생
- 테이블 크기에 따라 읽기 비용 선형 증가
- 물리자원(저장소 I/O 비용) 소비
WHERE 구에서 조건 분기 하는 사람 = 초보자
- SELECT 구만으로 조건 분기 가능
- CASE 사용으로 가능
select item_name, year,
case when year <= 2001 then price_tax_ex
when year >= 2002 then price_tax_in end as price
from Items;
9장. 집계와 조건 분기
집계 대상으로 조건 분기
UNION 방법
select prefecture,
sum(pop_men) as pop_men,
sum(pop_wom) as pop_wom
from ( select prefecture, pop as pop_men, null as pop_wom
from population
where gender = '1'
union
select prefecture, null as pop_men, pop as pop_wom
from population
where gender = '2') temp
group by prefecture;
- 이러한 방법 자체가 절차지향적 사고
- 테이블을 각자 select로 구하고, merge
- 테이블 풀 스캔 2 회
CASE 방법
select prefecture,
sum(case when gender = '1' then pop else 0 else) as pop_men,
sum(case when gender = '2' then pop else 0 else) as pop_wom
from population
group by prefecture;
- 간단한 쿼리
- 테이블 풀 스캔 1회
- 대부분의 경우 UNION으로 할 수 있는 쿼리는 CASE로도 가능 (아닌 경우도 있음)
집약 결과로 조건 분기
핵심
- where, having 에서 조건 분기하는 사람 = 뉴비
문제
- 조건
- 소속팀이 1개면 팀 이름 그대로 출력
- 소속팀 2개면 ‘2개를 겸무’ 출력
- 소속팀 3개 이상이면 ‘3개 이상을 겸무’ 출력
UNION 방법
- union으로 merge한 이상, 구문 레벨 분기일 뿐
- where과 큰 차이 없음
- case를 이용해서 식 레벨 분기로 바꿔야 함
- 테이블 풀 스캔 3회
select emp_name, max(team) as team
from employees
group by emp_name
having count(*) = 1
union
select emp_name, '2개를 겸무' as team
from employees
group by emp_name
having count(*) = 2
union
select emp_name, '3개 이상을 겸무' as team
from employees
group by emp_name
having count(*) >= 3;
CASE 방법
- 테이블 풀 스캔 1회
- 집약 함수 결과가 스칼라(분할 불가값)
select emp_name,
case when count(*) = 1 then max(team)
when count(*) = 2 then '2개 겸무'
when count(*) >= 3 then '3개 이상 겸무'
end as team
from employees
group by emp_name;
10장. 그래도 UNION 필요한 경우
UNION 사용할 수밖에 없는 경우
- SELECT 구문에서 사용하는 테이블이 다른 경우
- FROM 구에서 테이블 결합해 CASE 식으로 결과 얻을 수 있지만, 필요없는 결합임.
UNION을 쓰는게 성능적으로 더 좋은 경우
- 인덱스를 적절하게 쓴 경우
- 테이블 풀 스캔보다 성능 우세일 가능성
union 방법
- (dx, fx) 쌍으로 3개의 인덱스가 있는 상태
select key, name, d1, f1, d2, f2, d3, f3
from three
where d1 = '2013-11-01' and f1 = 't'
union
select key, name, d1, f1, d2, f2, d3, f3
from three
where d2 = '2013-11-01' and f2 = 't'
union
select key, name, d1, f1, d2, f2, d3, f3
from three
where d3 = '2013-11-01' and f3 = 't'
SELECT STATEMENT
SORT UNIQUE
UNION-ALL
TABLE ACCESS BY INDEX ROWID
INDEX RANGE SCAN
TABLE ACCESS BY INDEX ROWID
INDEX RANGE SCAN
TABLE ACCESS BY INDEX ROWID
INDEX RANGE SCAN
OR 방법
- 3회 인덱스 스캔 vs 1회 테이블 풀 스캔
- 테이블 크고, 조건 분기로 선택되는 레코드 수가 적으면 union 이 더 성능 우세
select key, name, d1, f1, d2, f2, d3, f3
from three
where (d1 = '날짜' and f1 = 't')
or (d2 = '날짜' and f2 = 't')
or (d3 = '날짜' and f3 = 't');
SELECT STATEMENT
TABLE ACCESS FULL
IN 방법
select key, name, d1, f1, d2, f2, d3, f3
from three
where ('날짜', 't') in ((d1, f1), (d2, f2), (d3, f3));
CASE 방법
select key, name, ...
from three
where case when d1 = '날짜' then f1
when d2 = '날짜' then f2
when d3 = '날짜' then f3
else null
end = 't';
11강. 절차 지향형과 선언형
구문 기반 vs 식 기반
- SQL 뉴비 = 절차 지향적 세계
- SQL 석유 = 선언적 세계
- 차이는 기본단위의 차이 =
구문
vs 식
- SQL은
CASE 식
으로 조건 분기
- SQL 구문 내부에도
식
- 암튼 SQL 조건분기는
식
으로 이뤄져 있다.
정리
- SQL 성능 = 저장소 I/O 감소 정도
- UNION 조건 분기 < CASE 조건 분기
- 테이블 스캔 감소시킬 수 있는 효자 CASE
- 구문에서 식으로 패러다임 전환