- IOT, 클러스터, 파티션은 테이블 랜덤액세스 최소화에 매우 효과적인 구조
루트블록의 각 레코드는 하위 노드를 가르키는 블록 주소를 갖는다.
가르키는 주소로 찾아간 블록에는 자신의 키값보다 크거나 같은 값을 갖는 레코드가 저장돼 있음을 의미.
루트 블록에는 키값을 갖지 않는 특별한 레코드가있다
가장 왼쪽에 있는 LMC(Leftmost Child) 레코드다.
LMC는 자식 노드 중 가장 왼쪽 끝에 위치한 블록을 가리킨다.
LMC가 가르키는 주소로 찾아간 블록에는 '키 값을 가진 첫번째 레코드 보다 작거나 같은 값'을 갖는 레코드가 저장되어있다.
수직적 탐색은 스캔 시작점을 찾는 과정
조건별 탐색루트 상세설명은 책 175P참조
아래조건들에대해 어떻게 탐색을할지 추측해볼것
--조건절1
WHERE C1='B'
--조건절2
WHERE C1='B'
AND C2 = 3
--조건절3
WHERE C1='B'
AND C2 >= 3
--조건절4
WHERE C1='B'
AND C2<=3
--조건절5
WHERE C1='B'
AND C2 BETWEEN 2 AND 3
--조건절6
WHERE C1 BETWEEN 'A' AND 'C'
AND C2 BETWEEN 2 AND 3
반드시 이해해야할 용어 두가지 '액세스조건' 과 '필터조건'
인덱스 액세스 조건 : 인덱스 스캔 범위를 결정하는 조건절, 인덱스 수직적 탐새을 통해 스캔 시작점을 결정하는데 영향, 인덱스 리프블록 스캔하다 어디서 멈출지 결정
인덱스 필터 조건 : 테이블로 액세스 할지결정하는 조건절
인덱스를 이용하든 테이블 full scan하든 테이블 액세스단계에서 처리되는 조건절은 모두 필터조건
테이블 필터조건 : 쿼리 수행 다음 단계로 전달하거나 최종 결과집합에 포함할지 결정
비용 = 인덱스 수직적 탐색 비용 + 인덱스 수평적 탐색 비용 + 테이블 랜덤 액세스 비용
= 인덱스 루트와 브랜치 레벨에서 읽히는 블록수 + 인덱스 리프블록을 스캔하는 과정에서 읽는 블록 수+ 테이블 액세스 과정에 읽는 블록 수
where 아파트시세코드 = :a
where 아파트시세코드 = :a and 평형 = :b
where 아파트시세코드 = :a and 평형 = :b and 평형타입 = :c
where 아파트시세코드 = :a and 평형 = :b and 평형타입 between :c and :d
SELECT 해당층, 평단가, 입력일, 해당동, 매물구분, 연사용일수, 중개업소코드
FROM 매물아파트매매
WHERE 아파트시세코드 = 'A01011350900056'
AND 평형 ='59'
AND 평형타입 ='A'
AND 인터넷매물 BETWEEN '1' AND '3'
ORDER BY 입력일
SELECT /*+ ordered use_nl(b)*/ b.해당층, b.평단가, b.입력일,
b.해당동, b.매물구분, b.연사용일수, b.중개업소코드
FROM 통합코드 a, 매물아파트매매 b
WHERE a.코드구분 = 'CD064' --인터넷매물구분
AND a.코드 between '1' and '3'
and b.인터넷매물 = a.코드
AND b.아파트시세코드 = 'A01011350900056'
AND b.평형 ='59'
AND b.평형타입 ='A'
AND b.인터넷매물 BETWEEN '1' AND '3'
ORDER BY 입력일
where 고객등급 between 'C' AND 'D'
AND 고객번호 = 123
SELECT *
FROM 고객별가입상품
WHERE 고객번호 = :CUST_NO
AND 상품ID IN ('NH00037', 'NH00041', 'NH00050')
SELECT /*+ num_index_keys(a 고객별가입상품_X1 1) */
*
FROM 고객별가입상품 a
WHERE 고객번호 = :CUST_NO
AND 상품ID IN ('NH00037', 'NH00041', 'NH00050')
BETWEEN이 더정확한 표현식인데 LIKE를선호하는이유는 코딩이 편리하기때문
둘다 범위검색 조건으로, 범위검색조건 사용시 비효율 원리가 똑같이 적용.
데이터 분포와 조건절 값에 따라 인덱스 스캔량이 서로 다를수 있다.
결론은 LIKE보다 BETWEEN을 사용하는게 낫다.
인덱스 [판매월+판매구분]순으로 구성, 판매구분은 'A'와 'B'두개값이 존재, 90%, 10%의 비중차지
<조건절1>
WHERE 판매월 BETWEEN '201901' AND '201912'
AND 판매구분 ='B'
<조건절2>
WHERE 판매월 LIKE '2019%'
AND 판매구분 ='B'
조건절1은 판매월=201901, 판매구분=B인 첫번째레코드에서 스캔시작
조건절2는 판매월이 201901인 첫번째레코드에서 스캔 시작, 혹시라도201900이있을수있기때문에 판매구분 B부터 시작못함.
항상 스캔이 어떻게진행될지 머리속에 데이터구조를 그려라. 조회조건별 건수확인도 중요.
인덱스 선두컬럼에 대한 옵션조건에 OR조건을 사용해선 안된다.
인덱스 액세스 조건으로 사용불가
인덱스 필터 조건으로도 사용불가
테이블 필터 조건으로만 사용가능.
인덱스 구성 컬럼중 하나이상이 NOT NULL컬럼이면 18C부터 인덱스 필터조건으로 사용가능.
OR조건을 이용한 옵션 조건 처리는 가급적 사용하지 않아야 한다.
NULL허용컬럼이더라도 결과집합을 보장
변별력이 좋은 필수 조건이 있는 상황에서 이들패턴사용은 나쁘지 않다.
필수조건 컬럼을 인덱스 선두에 두고 액세스 조건으로 사용하면, LIKE/BETWEEN이 인덱스 필터조건이어도 좋은성능을 낼 수 있다.
문제는 필수조건의 변별력이 좋지않을때(DISTINCT VALUE가 많을때)는 FULL SCAN이 유리할수있다.
LIKE/BETWEEN 패턴사용할때 점검해야할 4가지(BETWEEN은 1,2번)
고객ID LIKE '%' -- 전체가 조회되어야하나 NULL컬럼은 제외된다.
WHERE 고객명 LIKE :CUST_NM || '%' -- 김훈을넣으면 김훈과 김훈남등이 같이조회된다.
:CUST_ID변수에 값을 입력했는지에 따라 위아래 SQL중 하나만 실행되게 하는 방식
:CUST_ID변수에 값을 입력하든 안하든 인덱스를 최적으로 사용.(서로다른 인덱스사용)
SELECT FROM 거래
WHERE :CUST_ID IS NULL
AND 거래일자 BETWEEN :DT1 AND :DT2
UNION ALL
SELECT FROM 거래
WHERE :CUST_ID IS NOT NULL
AND 고객ID = :CUST_ID
AND 거래일자 BETWEEN :DT1 AND :DT2
UNION ALL보다 단순하면서 UNION ALL과 같은 성능을 낸다.
단점은 LIKE패턴처럼 NULL허용 컬럼에 사용할 수없다. 값이 NULL인 레코드가 결과집합에서 누락
SELECT * FROM DUAL WHERE NULL = NULL; 이 조회결과가없다는뜻
-- 고객ID가있고없을때 사용하는 INDEX가 다름
SELECT * FROM 거래
WHERE 고객ID = NVL(:CUST_ID, 고객ID)
AND 거래일자 BETWEEN :DT1 AND :DT2
SELECT * FROM 거래
WHERE 고객ID = DECODE(:CUST_ID, NULL, 고객ID, :CUST_ID)
AND 거래일자 BETWEEN :DT1 AND :DT2
select 회원번호, 회원명, 생년, 생월일, GET_ADDR(우편번호)
FROM 회원
WHERE 생월일 LIKE '01%'