#title 시작~종료 형태에서의 인덱스 정렬 순서에 따른 성능 영향 요소 [[TableOfContents]] ==== IP처리 기본적인 사항 ==== 우선 IP는 특수한 형태의 문자열 데이터다. 각각의 자리수의 구별이 있고, 또한 문자열이지만 범위 조회시에는 비교하기도 매우 까다롭다. 그러므로 많은 프로그램에서는 IP(문자열) <-> IP(4byte unsigned int)의 대조표를 가지고 있는 경우가 많이 있다. 하지만 mssql server (varchar(40)을 보아서 mssql 이라고 생각하겠다)에는 unsigned int가 없으므로 할 수 없이 bigint형을 사용하여야 한다. 어쨌든 데이터는 다음과 같은 식으로 들어 갈 수 있다. {{{ 문자열IP 정수IP ------------- ------- '58.16.0.0' 4154 ... ... ... }}} 서로간의 변환은 [http://www.databaser.net/moniwiki/wiki.php/IP%EA%B4%80%EB%A0%A8%ED%95%A8%EC%88%98 IP관련 함수]을 참고하기 바란다. ==== 디자인 방법 ==== 디자인 방법은 2가지다. * 방법1: 시작IP, 종료IP * 방법2: IP 방법2로 선택하면 입력시 시작IP, 종료IP 사이에 있는 IP를 모두 테이블에 입력하는 방법이다. 이것 저것 고민할 필요없이 입력부하만 고려하면 되겠다. 방법1을 선택하면 IP테이블의 크기가 상대적으로 줄어든다. 하지만 방법1를 생각한다면 몇가지 중요하게 생각해 주어야 할 것이 있다. 다음은 그 고려사항이다. 1. 범위검색을 어떻게 할 것인가? (데이터형의 결정) 2. 인덱스는 어떻게 설계해야 빠른가? (시작IP + 종료IP?? 또는 종료IP + 시작IP??) 3. 범위연산에 대한 상수 조건에 따른 I/O 차이 문제는? 다음의 예를 보면... {{{ create table test( b int, e int ); declare @i int; set @i = 0; while(@i < 100000) begin insert test values(@i, @i + 10); set @i = @i + 11; end; create index a on test(b, e); create index d on test(e, b); set nocount on select * from test where 10000 between b and e; select * from test where 20000 between b and e; select * from test where 30000 between b and e; select * from test where 40000 between b and e; select * from test where 50000 between b and e; select * from test where 60000 between b and e; select * from test where 70000 between b and e; select * from test where 80000 between b and e; select * from test where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 7, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 10, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 12, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 15, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 13, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 10, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 8, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ }}} 1십만중 중간인 5만에 가까워 질수록 I/O의 양이 증가하는 것을 볼 수 있다. 인덱스를 어떤 것을 쓰는지 확인해 보세요. a인덱스를 쓰다가 d인덱스를 쓰는 것을 볼 수 있다. 다음은 어떨까? {{{ select top 1 * from test where 10000 between b and e; select top 1 * from test where 20000 between b and e; select top 1 * from test where 30000 between b and e; select top 1 * from test where 40000 between b and e; select top 1 * from test where 50000 between b and e; select top 1 * from test where 60000 between b and e; select top 1 * from test where 70000 between b and e; select top 1 * from test where 80000 between b and e; select top 1 * from test where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 7, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 10, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 12, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 15, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ }}} 특이한 점이 발견되었다. 일단 위보다는 I/O가 줄었다. 하지만 똑같이 a인덱스를 쓰다가 d인덱스를 쓰는 것을 볼 수 있다. 혹시 인덱스가 많이 조각난 것일까? {{{ DBCC SHOWCONTIG ('dbo.test') DBCC SHOWCONTIG이(가) 'test' 테이블을 검색하는 중... 테이블: 'test'(325576198); 인덱스 ID: 0, 데이터베이스 ID: 12 TABLE 수준 검색을 수행했습니다. - 검색한 페이지................................: 21 - 검색한 익스텐트 ..............................: 4 - 익스텐트 스위치..............................: 3 - 익스텐트당 평균 페이지 수........................: 5.3 - 검색 밀도[최적:실제].......: 75.00% [3:4] - 익스텐트 검색 조각화 상태 ...................: 25.00% - 페이지당 사용 가능한 평균 바이트 수.....................: 736.6 - 평균 페이지 밀도(전체).....................: 90.90% DBCC 실행이 완료되었습니다. DBCC에서 오류 메시지를 출력하면 시스템 관리자에게 문의하십시오. }}} 인덱스 조각화 문제는 아닌 것으로 보인다. 그럼 다음과 같으면 어떨까? {{{ select top 1 * from test with(index(d)) where 10000 between b and e; select top 1 * from test with(index(d)) where 20000 between b and e; select top 1 * from test with(index(d)) where 30000 between b and e; select top 1 * from test with(index(d)) where 40000 between b and e; select top 1 * from test with(index(d)) where 50000 between b and e; select top 1 * from test with(index(d)) where 60000 between b and e; select top 1 * from test with(index(d)) where 70000 between b and e; select top 1 * from test with(index(d)) where 80000 between b and e; select top 1 * from test with(index(d)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ 우옷!! 전 구간에서 제대로 최소한의 I/O를 유지하고 있는 모습이다. 시작IP + 종료IP 클러스터드 인덱스라면 어떨까? {{{ create clustered index c on test(b, e); select top 1 * from test with(index(c)) where 10000 between b and e; select top 1 * from test with(index(c)) where 20000 between b and e; select top 1 * from test with(index(c)) where 30000 between b and e; select top 1 * from test with(index(c)) where 40000 between b and e; select top 1 * from test with(index(c)) where 50000 between b and e; select top 1 * from test with(index(c)) where 60000 between b and e; select top 1 * from test with(index(c)) where 70000 between b and e; select top 1 * from test with(index(c)) where 80000 between b and e; select top 1 * from test with(index(c)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 3, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 6, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 8, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 10, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 12, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 14, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 16, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 18, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 20, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ }}} 조금 나아졌기는 하지만 역시 최적은 아니다. 왜일까? 이유는 다음과 같다. {{{ select * from test with(index(a)) where 90000 between b and e; --b <= 90000 : 9십만건 --> 9십만건중 e <= 90000 조건을 검색 select * from test with(index(d)) where 90000 between b and e; --e >= 90000 : 1십만건 --> 1십만건중 b >= 90000 조건을 검색 select top 1 * from test with(index(a)) where 90000 between b and e; select top 1 * from test with(index(d)) where 90000 between b and e; -- 두 쿼리를 볼 때 SQL Server는 인덱스 조건자(인덱스에 포함되어 커버됨)는 -- 작은 숫자에서 높은 숫자로 찾는 것을 알 수 있다. -- 그러므로 종료 + 시작의 순서로 된 인덱스는 -- 바로 원하는 값을 찾을 수 있으므로 TOP 1 을 했을 경우 스캔을 바로 멈추므로 I/O의 차이가 난다. }}} 인덱스가 하나만 있다면? {{{ drop index test.c create clustered index c on test(b); select top 1 * from test with(index(c)) where 10000 between b and e; select top 1 * from test with(index(c)) where 20000 between b and e; select top 1 * from test with(index(c)) where 30000 between b and e; select top 1 * from test with(index(c)) where 40000 between b and e; select top 1 * from test with(index(c)) where 50000 between b and e; select top 1 * from test with(index(c)) where 60000 between b and e; select top 1 * from test with(index(c)) where 70000 between b and e; select top 1 * from test with(index(c)) where 80000 between b and e; select top 1 * from test with(index(c)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 2, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 7, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 9, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 11, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 13, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 15, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 17, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 19, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 22, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ drop index test.c create clustered index c on test(e); select top 1 * from test with(index(c)) where 10000 between b and e; select top 1 * from test with(index(c)) where 20000 between b and e; select top 1 * from test with(index(c)) where 30000 between b and e; select top 1 * from test with(index(c)) where 40000 between b and e; select top 1 * from test with(index(c)) where 50000 between b and e; select top 1 * from test with(index(c)) where 60000 between b and e; select top 1 * from test with(index(c)) where 70000 between b and e; select top 1 * from test with(index(c)) where 80000 between b and e; select top 1 * from test with(index(c)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ drop index test.c create clustered index c on test(b); select * from test with(index(c)) where 10000 between b and e; select * from test with(index(c)) where 20000 between b and e; select * from test with(index(c)) where 30000 between b and e; select * from test with(index(c)) where 40000 between b and e; select * from test with(index(c)) where 50000 between b and e; select * from test with(index(c)) where 60000 between b and e; select * from test with(index(c)) where 70000 between b and e; select * from test with(index(c)) where 80000 between b and e; select * from test with(index(c)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 7, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 9, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 11, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 13, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 15, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 17, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 19, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 22, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ drop index test.c create clustered index c on test(e); select * from test with(index(c)) where 10000 between b and e; select * from test with(index(c)) where 20000 between b and e; select * from test with(index(c)) where 30000 between b and e; select * from test with(index(c)) where 40000 between b and e; select * from test with(index(c)) where 50000 between b and e; select * from test with(index(c)) where 60000 between b and e; select * from test with(index(c)) where 70000 between b and e; select * from test with(index(c)) where 80000 between b and e; select * from test with(index(c)) where 90000 between b and e; /* 테이블 'test'. 검색 수 1, 논리적 읽기 수 22, 물리적 읽기 수 0, 미리 읽기 수 1, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 20, 물리적 읽기 수 0, 미리 읽기 수 4, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 18, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 16, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 14, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 12, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 10, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 8, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. 테이블 'test'. 검색 수 1, 논리적 읽기 수 5, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0. */ }}} 결론적으로 e(종료일자)에만 인덱스가 있으면 된다. clustered index아니라 covered index를 원한다면 종료일자+시작일자로 인덱스를 만들면 된다. ==== IP4, IP6문제에서의 데이터형 결정 ==== IP4, IP6는 그 포멧도 틀리고 또한 길이도 다르다. 하지만 binary로도 변경할 수 있기 때문에 varbinary(16)으로 IP4, IP6을 모두 커버할 수 있다. 그러므로 검색시는 binary를 문자열로 보여주기만 하면 된다. 문제에서는 True, False의 문제이므로 변환에 드는 비용은 극히 적을 것이다. 다른 경우도 대부분 데이터형의 변환비용은 연산이므로 CPU의 성능에 따라서 성능이 틀리겠지만 이 또한 성능요구사항에 크게 영향을 미치지 않으리라 생각한다. (이는 사용자 환경에 따라서 많이 틀려질 수도 있다.)