1. 사전 구성
SELECT *, CONVERT(INT, RIGHT(num,1)) AS code
INTO TB_DATA11
FROM
(SELECT ROW_NUMBER() OVER (ORDER BY A.number) AS num
, LEFT(CONVERT(CHAR(8), DATEADD(HH, A.number, '2020-01-01'), 112), 6) AS date
FROM master..spt_values A
, (SELECT *
FROM master..spt_values B
WHERE type = 'P' AND number <= 500) B
WHERE A.type = 'P'
) C
;
CREATE INDEX NIDX01_DATA11 ON TB_DATA11(code, date);
SELECT number AS id, number+2 AS code
INTO TB_CODE
FROM master..spt_values B
WHERE type = 'P' AND number BETWEEN 1 AND 10
;
CREATE INDEX NIDX01_CODE ON TB_CODE(code);
table_name |
index_name |
ndex_description |
Is_Unique |
indexed_columns |
included_columns |
TB_CODE |
NIDX01_CODE |
nonclustered |
0 |
code |
[NULL] |
TB_DATA11 |
NIDX01_DATA11 |
nonclustered |
0 |
code, date |
[NULL] |
2. AS-IS
SELECT A.code
FROM TB_CODE A
INNER JOIN TB_DATA11 B
ON A.code = B.code
AND B.date > '20200101'
GROUP BY A.code
ORDER BY A.code
;
- 최종 행 수가 7건 뿐임에도 [TB_DATA11] 테이블과 내부 조인이 수행되면서 약 45만건의 행을 읽고 이를 GROUP BY 절로 중복값을 제거하여 출력하게 된다.
3. TO-BE
SELECT tc.code
FROM TB_CODE tc
WHERE EXISTS (
SELECT 1
FROM TB_DATA11 td
WHERE td.code = tc.code
AND td.[date] > '20200101')
GROUP BY tc.code
ORDER BY tc.code
;
- [TB_CODE] 테이블의 [code] 컬럼 값이 [TB_DATA11] 테이블에 존재하는지 여부만 확인하면 되기 때문에 EXISTS 절을 고려해 볼 수 있다.
- EXISTS는 Semi Join으로 수행된다. 즉, 메인 쿼리 하나의 행에 대해서 서브 쿼리의 모든 행과 조인하지 않는다. 처음 조인에 성공하는 행을 만나면 스캔을 멈추고 해당 행을 최종 결과 셋에 포함한다.
- EXISTS 절을 활용하면 후행 테이블의 스캔 범위를 감소시킬 수 있고, 선행 테이블에 대해서만 중복을 제거하면서 I/O를 개선할 수 있다.
댓글