5.2 소트 발생을 방지하는 SQL 작성
SQL을 작성할 때는 소트(Sort) 연산이 발생하지 않도록 주의해야 합니다.
Union, Minus, Distinct 연산자는 중복 레코드를 제거하기 위해 소트 연산을 수행하므로, 필요한 경우에만 사용해야 합니다.
5.2.1 Union | Union All
UNION
을 사용하면 옵티마이저가 상위와 하위 두 집합 간의 중복을 제거하기 위해 소트 작업을 수행합니다. 반면, UNION ALL
은 중복을 확인하지 않고 두 집합을 단순히 결합하므로 소트 작업이 발생하지 않습니다.
가능하면 UNION ALL
을 사용하는 것이 좋습니다. 단, UNION
을 UNION ALL
로 변경하면 결과 집합이 달라질 수 있으므로 신중하게 판단해야 합니다.
아래 SQL은 UNION
상위와 하위 집합 사이에 인스턴스 중복 가능성이 없으므로 UNION ALL
을 사용하는 것이 효과적입니다.
SELECT 결제번호, ...
FROM 결제
WHERE 결제수단코드 = 'M' AND 결제일자 = '20200101'
UNION ALL
SELECT 결제번호, ...
FROM 결제
WHERE 결제수단코드 = 'C' AND 결제일자 = '20200101'
아래 SQL은 UNION 상위와 하위 집합 사이에 인스턴스 중복 가능성이 있습니다.
SELECT 결제번호, ...
FROM 결제
WHERE 결제일자 = '20200101'
UNION
SELECT 결제번호, ...
FROM 결제
WHERE 주문일자 = '20200101'
0 1 SELECT STATEMENT 1 0 SORT(UNIQUE) … 2 1 UNION-ALL …. 결제일자와 주문일자 조건은 상호 배타적이지 않기 때문에 중복이 발생할 수 있습니다.
소트 연산이 발생하지 않도록 하기 위해서는 다음과 같이 작성할 수 있습니다.
SELECT 결제번호, ...
FROM 결제
WHERE 결제일자 = '20200101'
UNION ALL
SELECT 결제번호, ...
FROM 결제
WHERE 주문일자 = '20200101'
AND 결제일자 <> '20200101'
0 1 SELECT STATEMENT 1 0 UNION-ALL 2 1 TABLE ACCESS … 3 2 INDEX (RANGE SCAN).. 4 1 TABLE ACCESS .. 5 4 INDEX .. ….
다만, 결제일자가 NULL을 허용하는 경우 조건절을 다음과 같이 변경해야 합니다.
AND (결제일자 <> '20200101' OR 결제일자 IS NULL)
또는 LNNVL 함수를 사용하여 다음과 같이 작성할 수 있습니다.
AND LNNVL(결제일자 = '20230101')
5.2.2 Exists 활용 중복 레코드를 제거하기 위해 DISTINCT 연산자를 사용하는 경우, 조건에 해당하는 모든 데이터를 읽어야 하기 때문에 부분 범위 처리가 불가능하며, 모든 데이터를 읽는 과정에서 많은 I/O가 발생합니다.
SELECT DISTINCT p.상품번호, p.상품명, p.상품가격, ...
FROM 상품 p, 계약 c
WHERE p.상품유형코드 = :pclscd
AND c.상품번호 = p.상품번호
AND c.계약일자 BETWEEN :dt1 AND :dt2
AND c.계약구분코드 = :ctpcd
이를 다음과 같이 변경할 수 있습니다.
SELECT p.상품번호, p.상품명, p.상품가격, ...
FROM 상품 p, 계약 c
WHERE p.상품유형코드 = :pclscd
AND EXISTS (
SELECT 'x'
FROM 계약 c
WHERE c.상품번호 = p.상품번호
AND c.계약일자 BETWEEN :dt1 AND :dt2
AND c.계약구분코드 = :ctpcd
)
DISTINCT를 사용하지 않았으므로 부분 범위 처리가 가능해집니다.
MINUS 연산자의 경우, NOT EXISTS 서브쿼리로 변경할 수 있습니다.
5.2.3 조인 방식 변경 SORT (ORDER BY)가 발생하는 경우, 인덱스를 이용해 소트 연산을 생략할 수 있습니다. NL(Nested Loop) 조인 방식을 사용하도록 조인 방식을 변경하면 최소한의 연산을 생략할 수 있어 부분 범위 처리가 가능해져 성능이 크게 향상됩니다.
SELECT /*+ LEADING(c) USE_NL(p) */
c.계약번호, ...
FROM 계약 c, 상품 p
WHERE ...