1. 사전구성
SELECT a.number id
, NEWID() txt
, DATEADD(dd, a.number-1, '2020-01-01') date
INTO TB_FSpool02
FROM master..spt_values a, master..spt_values b
WHERE a.type = 'P'
AND a.number BETWEEN 1 AND 10
AND b.type = 'P'
AND b.number BETWEEN 1 AND 5
;
CREATE CLUSTERED INDEX CIDX_FPool02 ON TB_FSpool02 (date);
SELECT ROW_NUMBER () OVER (ORDER BY a.number) id
, CAST(LEFT(a.number*b.high, 2) AS INT) value
INTO TB_SSpool02
FROM master..spt_values a, master..spt_values b
WHERE a.type = 'P'
AND a.number BETWEEN 1 AND 1000
AND b.type = 'P'
AND b.number BETWEEN 1 AND 1000
;
table_name |
index_name |
index_description |
Is_Unique |
indexed_columns |
included_columns |
TB_FSpool02 |
CIDX_FPool02 |
clustered |
0 |
date |
[NULL] |
All density |
Average Length |
Columns |
0.1000000015 |
8 |
date |
RANGE_HI_KEY |
RANGE_ROW |
EQ_ROWS |
DISTINCT_RANGE_ROWS |
AVG_RANGE_ROWS |
2020-01-01 00:00:00.000 |
0 |
5 |
0 |
1 |
2020-01-03 00:00:00.000 |
5 |
5 |
1 |
5 |
2020-01-05 00:00:00.000 |
5 |
5 |
1 |
5 |
2020-01-07 00:00:00.000 |
5 |
5 |
1 |
5 |
2020-01-09 00:00:00.000 |
5 |
5 |
1 |
5 |
2020-01-10 00:00:00.000 |
0 |
5 |
0 |
1 |
2. AS-IS
SELECT f.id, f.txt
, (SELECT s.value
FROM TB_SSpool02 s
WHERE f.id = s.id) value
FROM TB_FSpool02 f
WHERE date BETWEEN '2020-01-01' AND '2020-01-02'
;
- [TB_FSpool02] 테이블에서 [date] 컬럼 범위 조건에 해당하는 [id] 값은 1 또는 2로 중복되어 있다.
- 서브 쿼리에 동일한 값으로 반복되어 조인된다.
조인하는 과정에서 서브 쿼리의 인덱스 부재로 인해 Index Spool (Lazy Spool)을 동반한 전체 스캔이 반복되면서 I/O가 높게 사용된다.
- Index Spool (Lazy Spool)은 쿼리가 수행되는 동안에만 반복적으로 사용될 데이터를 임시 테이블에 저장한다.
반복적으로 읽어야 하는 테이블을 매번 전체 스캔하는 대신 값이 전달될 때 조인된 결과를 Spool에 추가한다.
동일한 값이 전달되면 기존에 생성한 Spool과 조인하면서 읽는 범위를 최소화 한다.
- 이렇게 생성된 Spool에 내부적으로 비 클러스터형 인덱스를 생성하여 데이터를 재사용할 때 인덱스 탐색으로 수행한다.
3. TO_BE
CREATE INDEX NIDX01_SSpool02 ON TB_SSpool02 (id) INCLUDE (value);
- Index Spool (Lazy Spool)은 데이터를 반복해서 읽어야 하는 구문에서 인덱스가 부재인 경우에 나타날 수 있다.
- 필요한 데이터만 인덱스 탐핵할 수 있도록 후행 테이블인 [TB_SSpool02]의 조인 조건 컬럼에 인덱스를 생성하면 I/O를 감소시킬 수 있다.
댓글