본문 바로가기

Oracle Database SQL Tuning Guide 12c Release 2 (12.2)

Chapter08.Optimizer Access Paths

8.1 Introduction to Access Paths
행 원본은 실행 계획의 한 단계에서 반환되는 행 집합입니다.
행 소스는 조인 또는 그룹화 조작의 표,보기 또는 결과 일 수 있습니다.
행 소스에서 행을 검색하기 위해 u 리에서 사용되는 기술인 액세스 경로와 같은 단 일 조작은 입력으로 단일 행 소스를 채택합니다.
예를 들어, 전체 테이블 스캔은 단일 행 소스의 행 검색입니다.
대조적으로 조인 작업은 2 진이며 2 개의 행 소스에서 입력을 수신합니다
데이터베이스는 서로 다른 관계형 데이터 구조에 대해 다른 액세스 경로를 사용합니다.
다음 표는 주요 데이터 구조에 대한 공통 액세스 경로를 요약 한 것입니다.
 
Table 8-1 Data Structures and Access Paths
-------------------------
표 참고
-------------------------
 
옵티마이 저가 서로 다른 가능한 실행 계획을 고려한 다음 각 계획에 비용을 할당합니다.
옵티마이 저는 최저 비용으로 플랜을 선택합니다.
일반적으로 인덱스 액세스 경로는 테이블 행의 작은 하위 집합을 검색하는 명령문에서 더 효율적이지만 전체 테이블 스캔은 테이블의 많은 부분을 액세스 할 때보다 효율적입니다.
 
8.2 Table Access Paths
테이블은 오라클 데이터베이스의 데이터 구성의 기본 단위입니다.
관계형 테이블이 가장 일반적인 테이블 유형입니다. 관계형 테이블에는 다음과 같은 조직 특성이 있습니다.
• 힙 - 구성 테이블은 특정 순서로 행을 저장하지 않습니다.
• 인덱스 구성 테이블은 기본 키 값에 따라 행을 정렬합니다.
• 외부 테이블은 메타 데이터가 데이터베이스에 저장되지만 데이터는 데이터베이스 외부에 저장되는 읽기 전용 테이블입니다.
 
이 절에서는 힙 - 조직 테이블에 대한 최적화 프로그램 액세스 경로를 설명하고 다음 주제를 포함합니다.
• About Heap-Organized Table Access
• Full Table Scans
• Table Access by Rowid
• Sample Table Scans
• In-Memory Table Scans
 
8.2.1 About Heap-Organized Table Access
기본적으로 테이블은 힙 (heap)으로 구성됩니다. 즉, 데이터베이스는 사용자가 지정한 순서가 아닌 가장 적합한 행을 배치합니다.
사용자가 행을 추가하면 데이터베이스는 데이터 세그먼트의 첫 번째 사용 가능한 여유 공간에 행을 배치합니다.
행이 삽입 된 순서대로 검색되는 것은 아닙니다.
 
8.2.1.1 Row Storage in Data Blocks and Segments: A Primer
데이터베이스는 행을 데이터 블록에 저장합니다. 테이블에서 데이터베이스는 블록의 하단 부분에 행을 쓸 수 있습니다.
Oracle Database는 블록 자체를 관리하기 위해 행 디렉토리와 테이블 디렉토리를 포함하는 블록 오버 헤드를 사용합니다.
범위는 논리적으로 인접한 데이터 블록으로 구성됩니다.
블록은 디스크에서 물리적으로 연속적이지 않을 수 있습니다.
세그먼트는 테이블 공간 내의 논리 저장 구조에 대한 모든 데이터를 포함하는 한 세트의 범위입니다.
예를 들어, Oracle Database는 하나 이상의 익스텐트를 할당하여 테이블에 대한 데이터 세그먼트를 형성합니다.
데이터베이스는 하나 이상의 Extent를 할당하여 테이블에 대한 인덱스 세그먼트를 형성합니다.
기본적으로 데이터베이스는 영구적으로 로컬로 관리되는 테이블 공간에 대해 자동 세그먼트 공간 관리 (ASSM)를 사용합니다.
세션이 먼저 테이블에 데이터를 삽입하면 데이터베이스는 비트 맵 블록을 포맷합니다.
비트 맵은 세그먼트의 블록을 추적합니다.
데이터베이스는 비트 맵을 사용하여 사용 가능한 블록을 찾은 다음 각 블록을 쓰기 전에 포맷합니다.
ASSM은 동시성 문제를 피하기 위해 블록 사이에 삽입을 분산시킵니다.
최고 수위 표시 (HWM)는 데이터 블록의 형식이 지정되지 않고 사용되지 않은 세그먼트의 한 지점입니다.
HWM 아래에서 블록은 형식이 지정되고 형식이 지정되거나 비어 있거나 형식이 지정되지 않을 수 있습니다.
낮은 최고 수위 표시 (낮은 HWM)는 모든 블록이 데이터 또는 이전에 포함 된 데이터를 포함하고 있기 때문에 모든 블록이 포맷 된 것으로 알려진 지점을 표시합니다.
전체 테이블 스캔 중에 데이터베이스는 서식이 지정된 것으로 알려진 낮은 HWM까지 모든 블록을 읽은 다음 세그먼트 비트 맵을 읽어 HWM과 낮은 HWM 사이의 어떤 블록을 포맷하고 안전하게 읽을지를 결정합니다.
이 블록은 포맷되지 않았기 때문에 데이터베이스는 HWM을 지나서 읽지 않는다는 것을 압니다.
 
See Also:
Oracle Database Concepts to learn about data block storage
 
8.2.1.2 Importance of Rowids for Row Access
힙 - 구성 테이블의 모든 행에는 행 조각의 실제 주소에 해당하는이 테이블에 고유 한 행 ID가 있습니다.
rowid는 행의 10 바이트 실제 주소입니다.
rowid는 특정 파일, 블록 및 행 번호를 가리 킵니다. 예를 들어, rowid AAAPecAAFAAAABSAAA에서 최종 AAA는 행 번호를 나타냅니다.
행 번호는 행 디렉토리 항목에 대한 색인입니다.
행 디렉토리 항목은 블록의 행 위치에 대한 포인터를 포함합니다.
데이터베이스는 때로는 블록의 맨 아래 부분에서 행을 이동할 수 있습니다.
예를 들어, 행 이동이 사용 가능한 경우, 파티션 키 갱신, 플래시백 테이블 조작, 축소 테이블 조작 등으로 행이 이동할 수 있습니다.
데이터베이스가 블록 내의 행을 이동하면 데이터베이스는 행 디렉토리 항목을 갱신하여 포인터를 수정합니다.
rowid는 일정하게 유지됩니다.
Oracle Database는 내부적으로 색인을 생성하기 위해 ROWID를 사용합니다.
예를 들어, B- 트리 색인의 각 키는 연관된 행의 주소를 가리키는 ROWID와 연관됩니다.
물리적 ROWID는 테이블 행에 대한 가장 빠른 액세스를 제공하여 데이터베이스가 단일 I / O에서 행을 검색 할 수있게합니다.
 
See Also: Oracle Database Concepts to learn about rowids
 
8.2.1.3 Direct Path Reads
직접 경로 읽기에서 데이터베이스는 디스크에서 버퍼를 PGA로 직접 읽어 들여 SGA를 완전히 건너 뜁니다.
다음 그림은 SGA에 버퍼를 저장하는 분산 읽기와 순차 읽기의 차이점과 직접 경로 읽기를 보여줍니다.
 
Figure 8-1 Direct Path Reads
 
Situations in which Oracle Database may perform direct path reads include:
• Execution of a CREATE TABLE AS SELECT statement
• Execution of an ALTER REBUILD or ALTER MOVE statement
• Reads from a temporary tablespace
• Parallel queries
• Reads from a LOB segment
 
See Also: Oracle Database Performance Tuning Guide to learn about wait events for direct path reads
 
8.2.2 Full Table Scans
전체 테이블 스캔은 테이블에서 모든 행을 읽은 다음 선택 기준에 맞지 않는 행을 필터링합니다.
 
8.2.2.1 When the Optimizer Considers a Full Table Scan
일] 적으로 옵티마이 저는 다른 액세스 경로를 사용할 수 없거나 다른 사용 가능한 액세스 경로가 더 높은 비용 일 때 전체 테이블 스 5을 선택합니다.
전체 테이블 스캔을 선택하는 일반적인 이유는 다음과 같습니다.
• 색인이 없습니다.
인덱스가없는 경우, 옵티마이 저는 전체 테이블 스캔을 사용합니다.
• 쿼리 조건자는 인덱스가 지정된 열에 함수를 적용합니다.
인덱스가 함수 기반 인덱스 ( "성능을위한 함수 기반 인덱스 사용 지침 (페이지 A-8)"참조)이 아니라면 데이터베이스는 함수가 적용된 열의 값이 아니라 열의 값을 인덱싱합니다.
일반적인 응용 프로그램 수준의 실수는 char_col과 같은 문자 열을 인덱싱 한 다음 WHERE char_col = 1과 같은 구문을 사용하여 열을 쿼리하는 것입니다.
데이터베이스는 암시 적으로 TO_NUMBER 함수를 상수 1에 적용하여 인덱스 사용을 방지합니다.
SELECT COUNT (*) 쿼리가 실행되고 인덱스가 있지만 인덱스 된 열에는 null이 포함되어 있습니다.
옵티마이 저가 색인을 사용하여 널 (NULL) 항목을 포함 할 수 없으므로 테이블 행 수를 계산할 수 없습니다.
쿼리 쿼리 조건은 B- 트리 인덱스의 앞쪽 가장자리를 사용하지 않습니다.
예를 들어, 인덱스가 직원 (first_name, last_name)에 존재할 수 있습니다.
사용자가 술어 WHERE last_name = 'KING'을 사용하여 조회를 발행하면, 컬럼 first_name이 술어에 없으므로 옵티마이 저가 색인을 선택할 수 없습니다. 그러나이 경우 옵티마이 저는 인덱스 스킵 스캔을 사용하도록 선택할 수 있습니다.
• 쿼리가 선택되지 않습니다.
옵티마이 저가 u 리에 테이블의 대부분 블록이 필요한 것으로 판별하면 인덱스를 사용할 수 있더라도 전체 테이블 스 5을 사용합니다. 전체 테이블 스캔은 더 큰 I / O 호출을 사용할 수 있습니다.
큰 I / O 호출을 줄이면 많은 작은 호출을 작성하는 것보다 저렴합니다.
• 테이블 통계가 오래되었습니다.
예를 들어, 테이블은 작았지만 지금은 커졌습니다.
테이블 통계가 오래되고 테이블의 현재 크기를 반영하지 않으면 옵티마이 저는 인덱스가 전체 테이블 스캔보다 가장 효율적임을 알지 못합니다. "옵티 마이저 통계 소개 (10-1 페이지)"를 참조하십시오.
• 테이블이 작습니다.
테이블이 상위 워터 마크 아래에 n 블록보다 적게 포함되어 있으면 (n은 DB_FILE_MULTIBLOCK_READ_COUNT 초기화 매개 변수의 설정과 동일 함) 전체 테이블 스캔은 인덱스 범위 스캔보다 저렴할 수 있습니다.
액세스되는 테이블 또는 인덱스의 비율에 관계없이 스캔 속도가 더 저렴할 수 있습니다.
• 테이블의 병렬 처리 수준이 높습니다.
테이블에 대한 높은 수준의 병렬 처리는 범위 스캔에 대해 전체 테이블 스캔을 위해 옵티 마이저를 왜곡합니다.
테이블에 대한 ALL_TABLES.DEGREE 컬럼의 값을 조회하여 병렬 처리 정도를 판별하십시오.
• 쿼리는 전체 테이블 검색 힌트를 사용합니다.
FULL (테이블 별명)은 옵티마이 저가 전체 테이블 스캔을 사용하도록 지시합니다.
 
8.2.2.2 How a Full Table Scan Works
전체 테이블 스캔에서, 데이터베이스는 상위 워터 마크 (water mark) 아래에있는 모든 형식화 된 블록을 순차적으로 읽습니다. 데이터베이스는 각 블록을 한 번만 읽습니다.
다음 그래픽은 스캔이 상위 워터 마크 (water mark) 아래의 형식이 지정되지 않은 블록을 스킵하는 방법을 보여주는 표 세그먼트의 스캔을 보여줍니다.
Figure 8-2 High Water Mark
 
블록이 인접 해 있기 때문에 데이터베이스는 다중 블록 읽기라는 단일 블록보다 큰 I / O 호출을 만들어 스캔 속도를 높일 수 있습니다.
읽기 호출의 크기는 한 블록에서 DB_FILE_MULTIBLOCK_READ_COUNT 초기화 매개 변수로 지정된 블록 수까지의 범위입니다.
예를 들어,이 매개 변수를 4로 설정하면 데이터베이스가 단일 호출에서 최대 4 개의 블록을 읽을 수 있습니다.
전체 테이블 스캔 중에 블록을 캐시하는 알고리즘은 복잡합니다.
예를 들어, 데이터베이스는 테이블이 작거나 큰지에 따라 블록을 다르게 캐시합니다.
 
See Also:
• "Table 19-1 (page 19-3)"
• Oracle Database Concepts for an overview of the default caching mode
• Oracle Database Reference to learn about the DB_FILE_MULTIBLOCK_READ_COUNT initialization parameter
 
8.2.2.3 Full Table Scan: Example
다음 명령문은 hr.employees 테이블에서 4000 이상의 급여를 쿼리합니다.
 
SELECT salary
FROM hr.employees
WHERE salary > 4000;
 
Example 8-1 Full Table Scan
다음 계획은 DBMS_XPLAN.DISPLAY_CURSOR 기능을 사용하여 검색되었습니다.
급여 열에는 색인이 없으므로, 옵티마이 저는 색인 범위 스캔을 사용할 수 없으므로 전체 테이블 스캔을 사용합니다.
 
SQL_ID 54c20f3udfnws, child number 0
-------------------------------------
select salary from hr.employees where salary > 4000
Plan hash value: 3476115102
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 98 | 6762 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SALARY">4000)
 
8.2.3 Table Access by Rowid
rowid는 데이터의 저장 위치를 내부적으로 표현한 것입니다.
행의 ROWID는 해당 블록에있는 행의 행 위치와 위치를 포함하는 데이터 파일과 데이터 블록을 지정합니다.
rowid를 지정하여 행을 찾는 것은 데이터베이스에서 행의 정확한 위치를 지정하기 때문에 단일 행을 검색하는 가장 빠른 방법입니다.
 
노트 :
Rowid는 버전간에 변경할 수 있습니다.
행을 이동할 수 있으므로 위치를 기반으로 데이터에 액세스하는 것은 좋지 않습니다.
 
See Also: Oracle Database Development Guide to learn more about rowids
 
8.2.3.1 When the Optimizer Chooses Table Access by Rowid
대부분의 경우, 데이터베이스는 하나 이상의 인덱스 스캔 후 rowid에 의해 테이블에 액세스합니다.
그러나 rowid에 의한 테이블 액세스는 모든 인덱스 스캔을 따를 필요는 없습니다.
색인에 필요한 모든 열이 들어 있으면 rowid에 의한 액세스가 발생하지 않을 수 있습니다.
 
8.2.3.2 How Table Access by Rowid Works
rowid에 의해 테이블에 액세스하기 위해 데이터베이스는 다음 단계를 수행합니다.
1. WHERE 절에서 하나 이상의 인덱스의 인덱스 스 5을 통해 선택된 행의 ROWID를 확보합니다.
인덱스에없는 명령문의 열에는 테이블 액세스가 필요할 수 있습니다.
 
2. rowid를 기초로 테이블에서 선택된 각 행을 찾습니다.
 
8.2.3.3 Table Access by Rowid: Example
다음 쿼리를 실행한다고 가정합니다.
 
SELECT *
FROM employees
WHERE employee_id > 190;
 
다음 계획의 2 단계는 hr.employees 테이블에서 emp_emp_id_pk 색인의 범위 스캔을 보여줍니다.
데이터베이스는 인덱스에서 얻은 ROWID를 사용하여 employees 테이블에서 해당 행을 찾은 다음 검색합니다.
1 단계에 표시된 BATCHED 액세스는 데이터베이스가 색인에서 몇 개의 ROWID를 검색 한 다음 블록 순서로 행에 액세스하려고 시도하여 클러스터링을 향상시키고 데이터베이스가 블록에 액세스해야하는 횟수를 줄입니다.
 
--------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time|
--------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |2(100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED|EMPLOYEES |16|1104|2 (0)|00:00:01|
|*2| INDEX RANGE SCAN |EMP_EMP_ID_PK|16| |1 (0)|00:00:01|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID">190)
 
8.2.4 Sample Table Scans
샘플 표 스캔은 조인 및 뷰와 관련된 명령문과 같은 복잡한 SELECT 문 또는 단순한 테이블에서 무작위로 데이터 샘플을 검색합니다.
 
8.2.4.1 When the Optimizer Chooses a Sample Table Scan
명령문 FROM 절이 SAMPLE 키워드를 포함 할 때 데이터베이스는 샘플 표 스캔을 사용합니다.
SAMPLE 절의 형식은 다음과 같습니다.
• SAMPLE (sample_percent)
데이터베이스는 테이블의 지정된 비율의 행을 읽어 샘플 테이블 스캔을 수행합니다.
• 샘플 블록 (sample_percent)
데이터베이스는 지정된 백분율의 테이블 블록을 읽어 샘플 테이블 스캔을 수행합니다.
 
sample_percent는 샘플에 포함 할 전체 행 또는 블록 수의 백분율을 지정합니다.
값은 .000001 ~ 100까지의 범위 내에 있어야합니다.
이 백분율은 블록 샘플링에서 각 행 또는 각 행의 클러스터가 샘플에 대해 선택 될 확률을 나타냅니다.
데이터베이스가 정확하게 sample_percent 행을 검색한다는 것을 의미하지는 않습니다.
 
노트 :
블록 샘플링은 전체 테이블 스캔 또는 인덱스 고속 전체 스캔 중에 만 가능합니다.
보다 효율적인 실행 경로가 존재하면 데이터베이스는 블록을 샘플링하지 않습니다.
특정 테이블이나 인덱스에 대한 블록 샘플링을 보장하려면 FULL 또는 INDEX_FFS 힌트를 사용하십시오.
 
See Also:
• "Influencing the Optimizer with Hints (page 19-11)"
• Oracle Database SQL Language Reference to learn about the SAMPLE clause
 
8.2.4.2 Sample Table Scans: Example
예제 8-2 (8-10 페이지)에서는 샘플 테이블 스캔을 사용하여 직원 테이블의 1 %에 액세스하여 행 대신 블록별로 샘플링합니다.
 
Example 8-2 Sample Table Scan
SELECT * FROM hr.employees SAMPLE BLOCK (1);
 
The EXPLAIN PLAN output for this statement might look as follows:
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 68 | 3 (34)|
| 1 | TABLE ACCESS SAMPLE | EMPLOYEES | 1 | 68 | 3 (34)|
-------------------------------------------------------------------------
 
8.2.5 In-Memory Table Scans
Oracle Database 12c Release 1 (12.1.0.2)부터 인 메모리 스캔은 인 - 메모리 컬럼 스토어 (IM 컬럼 스토어)에서 일부 또는 모든 로우를 검색합니다.
IM 컬럼 스토어는 빠른 스캔을 위해 최적화 된 특수 컬럼 형식으로 테이블 및 파티션의 사본을 저장하는 선택적 SGA 영역입니다.
 
See Also: Oracle Database In-Memory Guide for an introduction to the IM column store
 
8.2.5.1 When the Optimizer Chooses an In-Memory Table Scan
Oracle Database 12c Release 1 (12.1.0.2)부터 옵티마이 저 비용 모델은 IM 컬럼 스토어의 컨텐츠를 완벽하게 인식합니다.
사용자가 IM 열 저장소의 테이블을 참조하는 쿼리를 실행하면 옵티마이 저는 메모리 내 테이블 검색을 포함하여 가능한 모든 액세스 방법의 비용을 계산하고 최저 비용으로 액세스 방법을 선택합니다.
 
8.2.5.2 In-Memory Query Controls
다음 데이터베이스 초기화 매개 변수는 메모리 내 기능에 영향을줍니다.
• INMEMORY_QUERY
이 매개 변수는 세션 또는 시스템 수준에서 데이터베이스에 대한 메모리 내 쿼리를 사용하거나 사용하지 않도록 설정합니다.
이 매개 변수는 IM 열 저장소를 사용하거나 사용하지 않고 작업 부하를 테스트하려는 경우 유용합니다.
• OPTIMIZER_INMEMORY_AWARE
이 매개 변수는 옵티 마이저 비용 모델, 테이블 확장, 블룸 필터 등에 대해 수행 된 메모리 내 향상을 모두 사용 가능 (TRUE) 또는 사용 불가능 (FALSE)합니다.
매개 변수를 FALSE로 설정하면 옵티마이 저가 SQL 문의 최적화 중 테이블의 메모리 특성을 + 시합니다.
• OPTIMIZER_FEATURES_ENABLE
12.1.0.2보다 낮은 값으로 설정하면이 매개 변수는 OPTIMIZER_INMEMORY_AWARE를 FALSE로 설정하는 것과 동일한 효과를 갖습니다.
메모리 내 쿼리를 사용하거나 사용하지 않도록 설정하려면 INMEMORY_QUERY 초기화 매개 변수에 해당하는 쿼리 당 INMEMORY 또는 NO_INMEMORY 힌트를 지정하면됩니다.
SQL 문이 INMEMORY 힌트를 사용하지만 참조하는 개체가 IM 열 저장소에 이미로드되어 있지 않은 경우 데이터베이스는 문을 실행하기 전에 개체가 IM 열 저장소에 채워지기를 기다리지 않습니다.
그러나 개체의 초기 액세스는 IM 열 저장소의 개체 채우기를 트리거합니다.
 
See Also:
• Oracle Database Reference to learn more about the INMEMORY_QUERY, OPTIMIZER_INMEMORY_AWARE, and OPTIMIZER_FEATURES_ENABLE initialization parameters
• Oracle Database SQL Language Reference to learn more about the INMEMORY hints
 
8.2.5.3 In-Memory Table Scans: Example
다음 예는 oe.product_information 테이블에 대한 INMEMORY HIGH 옵션으로 변경된 쿼리를 보여줍니다.
 
Example 8-3 In-Memory Table Scan
 
SELECT *
FROM oe.product_information
WHERE list_price > 10
ORDER BY product_id;
 
이 명령문에 대한 계획은 다음과 같을 수 있습니다. 2 단계의 INMEMORY 키워드는 일부 또는 모든 오브젝트가 IM 열 저장소에서 액세스되었음을 나타냅니다.
 
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
 
SQL_ID 2mb4h57x8pabw, child number 0
-------------------------------------
select * from oe.product_information where list_price > 10 order byproduct_id
Plan hash value: 2256295385
--------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes |TempSpc|Cost(%CPU)|Time|
--------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | |21 (100)| |
| 1| SORT ORDER BY | | 285| 62415|82000|21 (5)|00:00:01|
|*2| TABLE ACCESS INMEMORY FULL| PRODUCT_INFORMATION | 285| 62415| | 5 (0)|00:00:01|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - inmemory("LIST_PRICE">10)
filter("LIST_PRICE">10)
 
8.3 B-Tree Index Access Paths
인덱스는 테이블 또는 테이블 클러스터와 관련된 선택적 구조로서 때로는 데이터 액세스를 가속화 할 수 있습니다.
테이블의 하나 이상의 열에 인덱스를 작성하면 테이블에서 임의로 분산 된 작은 행 세트를 검색 할 수있는 경우가 있습니다.
인덱스는 디스크 I / O를 줄이는 여러 방법 중 하나입니다.
 
See Also:
• Oracle Database Concepts for an overview of indexes
• Oracle Database Administrators Guide to learn how to manage indexes
 
8.3.1 About B-Tree Index Access
균형 잡힌 나무의 약자 인 B- 트리는 가장 일반적인 데이터베이스 인덱스 유형입니다.
B- 트리 색인은 값으로 정렬 된 값 목록으로 범위로 나뉩니다.
키를 행 또는 행 범위와 연관시킴으로써 B- 트리는 완전 일치 및 범위 검색을 포함하여 광범위한 쿼리에 대해 우수한 검색 성능을 제공합니다.
 
8.3.1.1 B-Tree Index Structure
B- 트리 색인에는 검색을위한 분기 블록과 값을 저장하는 리프 블록이라는 두 가지 유형의 블록이 있습니다.
다음 그래픽은 B-tree 인덱스의 논리 구조를 보여줍니다.
분기 블록은 두 개의 키 사이에 분기 결정을 내리는 데 필요한 최소 키 접 두부를 저장합니다.
리프 블록은 모든 색인 된 데이터 값과 실제 행을 찾는 데 사용되는 해당 rowid를 포함합니다.
각 색인 항목은 (key, rowid)별로 정렬됩니다. 리프 블록은 이중으로 링크됩니다.
 
Figure 8-3 B-Tree Index Structure
 
8.3.1.2 How Index Storage Affects Index Scans
비트 맵 인덱스 블록은 인덱스 세그먼트의 어느 위치 에나 나타날 수 있습니다.
그림 8-3 (8-13 페이지)은 서로 인접한 리프 블록을 보여줍니다. 예를 들어,
1-10 블록은 11-19 블록 옆과 그 앞에 있습니다. 이 시퀀싱은
색인 항목을 연결하는 연결 목록 그러나 인덱스 블록을 저장할 필요는 없습니다.
인덱스 세그먼트 내에서의 순서 예를 들어, 246-250 블록이 나타날 수 있습니다.
1-10 블록 바로 앞을 포함하여 세그먼트의 아무 곳이나. 이러한 이유로,
순서화 된 인덱스 스 5은 단일 블록 I / O를 수행해야합니다. 데이터베이스는 인덱스를 읽어야합니다.
다음에 읽어야하는 색인 ​​블록을 판별하십시오.
인덱스 블록 본문은 테이블 행처럼 인덱스 항목을 힙에 저장합니다. 에 대한
예를 들어, 값 10이 테이블에 처음 삽입되면 키 10을 갖는 인덱스 항목
색인 블록의 맨 아래에 삽입 될 수 있습니다. 테이블에 0이 삽입되면,
키 0에 대한 색인 항목이 항목의 맨 위에 10에 삽입 될 수 있습니다. 따라서
블록 본문의 색인 항목은 키 순서로 저장되지 않습니다. 그러나 색인 내에서
블록에서 행 헤더는 키 순서로 레코드를 저장합니다. 예를 들어,
헤더는 키 0을 갖는 인덱스 엔트리를 가리킨다.
이는 키가 10 인 색인 항목을 가리 킵니다. 따라서 색인 스캔은 행 헤더를 읽을 수 있습니다
범위 스캔을 시작하고 끝낼 위치를 결정하고 블록의 모든 항목을 읽지 않아도됩니다.
 
See Also: Oracle Database Concepts to learn about index blocks
 
8.3.1.3 Unique and Nonunique Indexes
고유하지 않은 색인에서 데이터베이스는 추가 열로 키에 추가하여 rowid를 저장합니다.
항목은 키를 고유하게 만들기 위해 길이 바이트를 추가합니다.
예를 들어, 그림 8-3 (8-13 페이지)에 표시된 고유하지 않은 색인의 첫 번째 색인 키는 0이 아닌 0, rowid 쌍입니다.
데이터베이스는 인덱스 키 값별로 정렬 한 다음 rowid를 오름차순으로 정렬합니다.
 
예를 들어, 항목은 다음과 같이 정렬됩니다.
0,AAAPvCAAFAAAAFaAAa
0,AAAPvCAAFAAAAFaAAg
0,AAAPvCAAFAAAAFaAAl
2,AAAPvCAAFAAAAFaAAm
 
고유 색인에서 색인 키에는 rowid가 포함되지 않습니다.
데이터베이스는 인덱스 키 값 (예 : 012 등)별로 데이터를 정렬합니다.
 
See Also: Oracle Database Concepts for an overview of unique and nonunique indexes
 
8.3.1.4 B-Tree Indexes and Nulls
B- 트리 색인은 완전한 Null 키를 절대로 저장하지 않으므로 옵티마이 저가 액세스 경로를 선택하는 방법에 중요합니다.
이 규칙의 결과는 단일 열 B- 트리 색인이 절대로 null을 저장하지 않는다는 것입니다.
예를 들어 설명 할 수 있습니다.
hr.employees 테이블에는 employee_id에 기본 키 인덱스가 있고 department_id에 고유 인덱스가 있습니다.
department_id 열에는 null이 포함될 수 있으므로 null 허용 열이되지만 employee_id 열은 포함 할 수 없습니다.
 
SQL> SELECT COUNT(*FROM employees WHERE department_id IS NULL;
COUNT(*)
----------
1
 
SQL> SELECT COUNT(*FROM employees WHERE employee_id IS NULL;
COUNT(*)
----------
0
 
다음 예제는 옵티마이 저가 hr.employees의 모든 부서 ID 조회에 대한 전체 테이블 스캔을 선택 함을 보여줍니다.
인덱스가 테이블의 모든 행에 대한 항목을 포함하지는 않으므로 옵티마이 저는 employees.department_id에서 인덱스를 사용할 수 없습니다.
 
SQL> EXPLAIN PLAN FOR SELECT department_id FROM employees;
Explained.
 
SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3476115102
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 321 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| EMPLOYEES | 107 | 321 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------
8 rows selected.
 
다음 예는 Null이 아닌 모든 행이 인덱싱되므로 Optimizer가 특정 부서 ID의 쿼리에 대해 department_id의 인덱스를 사용할 수 있음을 보여줍니다.
SQL> EXPLAIN PLAN FOR SELECT department_id FROM employees WHERE department_id=10;
Explained.
 
SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 67425611
--------------------------------------------------------------------------------
| Id| Operation | Name | Rows |Bytes| Cost (%CPU)| Time |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 1 (0)| 00:0 0:01|
|*1 | INDEX RANGE SCAN| EMP_DEPARTMENT_IX | 1 | 3 | 1 (0)| 00:0 0:01|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
1 - access("DEPARTMENT_ID"=10)
 
다음 예는 술어가 널 값을 제외 할 때 옵티마이 저가 인덱스 스 5을 선택 함을 보여줍니다.
 
SQL> EXPLAIN PLAN FOR SELECT department_id 
FROM employees 
WHERE department_id IS NOT NULL;
Explained.
 
SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1590637672
--------------------------------------------------------------------------------
| Id| Operation | Name | Rows|Bytes| Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 106 | 318 | 1 (0)| 00:0 0:01 |
|*1 | INDEX FULL SCAN | EMP_DEPARTMENT_IX | 106 | 318 | 1 (0)| 00:0 0:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
1 - filter("DEPARTMENT_ID" IS NOT NULL)
 
8.3.2 Index Unique Scans
인덱스 고유 스캔은 최대 하나의 rowid를 리턴합니다.
 
8.3.2.1 When the Optimizer Considers Index Unique Scans
인덱스 고유 스 5에는 동등 술어가 필요합니다.
특히 데이터베이스는 다음 조건이 적용되는 경우에만 고유 한 검색을 수행합니다.
• 쿼리 조건자는 WHERE prod_id = 10과 같은 항등 연산자를 사용하여 고유 인덱스 키의 모든 열을 참조합니다.
• SQL 문은 CREATE UNIQUE INDEX 문으로 생성 된 인덱스에 의해 참조되는 열에 동등 술어를 포함합니다.
고유 또는 기본 키 제한 조건은 인덱스 고유 스 5을 작성하기에 충분하지 않습니다.
고유하지 않은 색인이있는 컬럼에 기본 키 제한 조건을 작성하여 색인 고유 스캔 대신 색인 범위 스캔을 작성하는 다음 예제를 고려하십시오.
 
CREATE TABLE t_table(numcol INT);
 
CREATE INDEX t_table_idx ON t_table(numcol);
 
ALTER TABLE t_table ADD CONSTRAINT t_table_pk PRIMARY KEY(numcol);
 
SET AUTOTRACE TRACEONLY EXPLAIN
SELECT * FROM t_table WHERE numcol = 1;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 868081059
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 1 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| T_TABLE_IDX | 1 | 13 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("NUMCOL"=1)
 
INDEX (별칭 index_name) 힌트를 사용하여 사용할 인덱스를 지정할 수 있지만 특정 유형의 인덱스 액세스 경로는 지정할 수 없습니다.
 
See Also:
• Oracle Database Concepts for more details on index structures and for detailed information on how a B-tree is searched
• Oracle Database SQL Language Reference to learn more about the INDEX hint
 
8.3.2.2 How Index Unique Scans Work
검색은 지정된 키를 순서대로 검색합니다.
인덱스 고유 스 5은 두 x 째 레코드가 가능하지 않기 때.에 첫 x 째 레코드를 찾자 마자 처리를 중지합니다.
데이터베이스는 색인 항목에서 ROWID를 확보 한 다음 ROWID에 의해 지정된 행을 검색합니다.
다음 그림은 색인 고유 스캔을 보여줍니다.
이 명령문은 기본 키 색인이있는 prod_id 열에 제품 ID 19에 대한 레코드를 요청합니다.
 
Figure 8-4 Index Unique Scan
 
8.3.2.3 Index Unique Scans: Example
이 예에서는 고유 스캔을 사용하여 products 테이블에서 행을 검색합니다.
다음 문은 sh.products 테이블에서 제품 19에 대한 레코드를 쿼리합니다.
 
SELECT *
FROM sh.products
WHERE prod_id = 19;
 
기본 키 인덱스는 products.prod_id 열에 존재하고 WHERE 절은 항등 연산자를 사용하여 모든 열을 참조하므로 옵티마이 저는 고유 한 스캔을 선택합니다.
 
SQL_ID 3ptq5tsd5vb3d, child number 0
-------------------------------------
select * from sh.products where prod_id = 19
Plan hash value: 4047888317
--------------------------------------------------------------------------------
| Id| Operation | Name |Rows|Bytes|Cost (%CPU)|Time |
--------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 1 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID| PRODUCTS | 1 | 173 | 1 (0)| 00:00:01|
|* 2| INDEX UNIQUE SCAN | PRODUCTS_PK | 1 | | 0 (0)| |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("PROD_ID"=19)
 
8.3.3 Index Range Scans
인덱스 범위 스캔은 값의 정렬 된 스캔입니다.
스캔 범위는 양면에서 제한 될 수 있거나 한면 또는 양면에서 제한되지 않을 수 있습니다.
일반적으로 옵티마이 저는 선택 쿼리에 대해 범위 스캔을 선택합니다.
기본적으로 데이터베이스는 인덱스를 오름차순으로 저장하고 같은 순서로 스캔합니다.
예를 들어, 술어 department_id> = 20 인 쿼리는 범위 스캔을 사용하여 인덱스 키 203040 등으로 정렬 된 행을 반환합니다.
여러 색인 항목이 동일한 키를 갖는 경우, 데이터베이스는 rowid에 의해 오름차순으로 0을 리턴하므로 0, AAAPvCAAFAAAAFaAAa 뒤에 0, AAAPvCAAFAAAAFaAAg 등이옵니다.
내림차순 인덱스 범위 스캔은 데이터베이스가 내림차순으로 행을 반환한다는 점을 제외하고는 인덱스 범위 스캔과 동일합니다.
일반적으로 데이터베이스는 내림차순으로 데이터를 정렬 할 때 또는 지정된 값보다 작은 값을 찾을 때 내림차순 스캔을 사용합니다.
 
See Also: "Selectivity (page 4-7)"
 
8.3.3.1 When the Optimizer Considers Index Range Scans
인덱스 범위 스캔의 경우 인덱스 키에 대해 여러 값을 사용할 수 있어야합니다.
특히 다음과 같은 경우 옵티마이 저가 인덱스 범위 스캔을 고려합니다.
• 색인의 하나 이상의 선행 열이 조건에 지정됩니다.
조건은 하나 이상의 표현식과 논리 (부울) 연산자의 조합을 지정하고 TRUE, FALSE 또는 UNKNOWN 값을 반환합니다.
조건의 예는 다음과 같습니다.
– department_id = :id
– department_id < :id
– department_id > :id
– AND의 department_id> : low AND department_id <: hi와 같이 인덱스의 열을 앞선 조건으로 결합합니다.
 
노트 :
옵티마이 저가 범위 스캔을 고려하려면 col1 LIKE '% ASD'형식의 와일드 카드 검색이 선행 위치에 있지 않아야합니다.
• 인덱스 키에는 01 또는 그 이상의 값을 사용할 수 있습니다.
 
팁:
정렬 된 데이터가 필요한 경우 ORDER BY 절을 사용하고 인덱스를 사용하지 마십시오.
인덱스가 ORDER BY 절을 만족시킬 수 있으면 옵티마이 저는이 옵션을 사용하므로 정렬을 피할 수 있습니다.
옵티마이 저는 인덱스가 ORDER BY DESCENDING 절을 만족할 수있을 때 인덱스 범위 스캔이 내림차순으로 간주합니다.
옵티마이 저가 전체 테이블 스캔 또는 다른 인덱스를 선택하면,이 액세스 경로를 강요하기위한 힌트가 필요할 수 있습니다.
INDEX (tbl_alias ix_name) 및 INDEX_DESC (tbl_alias ix_name) 힌트는 옵티마이 저가 특정 인덱스를 사용하도록 지시합니다.
 
See Also: Oracle Database SQL Language Reference to learn more about the INDEX and INDEX_DESC hints
 
8.3.3.2 How Index Range Scans Work
인덱스 범위 스캔 중에 Oracle Database는 루트에서 지점으로 진행합니다.
일반적으로 스캔 알고리즘은 다음과 같습니다.
1. 루트 블록을 읽습니다.
2. 분기 블록을 읽습니다.
3. 모든 데이터가 검색 될 때까지 다음 단계를 교대하십시오.
 a. rowid를 얻기 위해 리프 블록을 읽는다.
 b. 행을 검색하기 위해 테이블 ​​블록을 읽습니다.
 
노트 :
경우에 따라 인덱스 스캔은 인덱스 블록 집합을 읽고 ROWID를 정렬 한 다음 테이블 블록 집합을 읽습니다.
따라서 인덱스를 스캔하기 위해 데이터베이스는 리프 블록을 통해 앞뒤로 이동합니다.
예를 들어 20과 40 사이의 ID를 검색하면 가장 낮은 키 값이 20 이상인 첫 번째 리프 블록을 찾습니다.
검색은 40보다 큰 값을 찾을 때까지 리프 노드의 링크 된 목록을 통해 가로로 진행 한 다음 중지합니다.
다음 그림은 오름차순으로 인덱스 범위 스캔을 보여줍니다.
성명은 고유하지 않은 색인이있는 department_id 열에 20이라는 값으로 직원 레코드를 요청합니다.
이 예에서 부서 20에 대한 2 개의 색인 항목이 있습니다.
 
Figure 8-5 Index Range Scan
 
8.3.3.3 Index Range Scan: Example
이 예에서는 인덱스 범위 스캔을 사용하여 employees 테이블에서 값 집합을 검색합니다.
다음 명령문은 급여가 1000보다 큰 부서 20의 직원에 대한 레코드를 조회합니다.
 
SELECT *
FROM employees
WHERE department_id = 20
AND salary > 1000;
 
앞의 쿼리는 카디널리티가 낮기 때문에 (행 수가 적음) 쿼리에서 department_id 열의 인덱스를 사용합니다.
데이터베이스는 인덱스를 스캔하고 employees 테이블에서 레코드를 페치 한 다음 급여> 1000 필터를 이러한 반입 된 레코드에 적용하여 결과를 생성합니다.
 
SQL_ID brt5abvbxw9tq, child number 0
-------------------------------------
SELECT * FROM employees WHERE department_id = 20 AND salary > 1000
Plan hash value: 2799965532
-------------------------------------------------------------------------------------------
|Id | Operation | Name |Rows|Bytes|Cost(%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
|*1 | TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES | 2 | 138 | 2 (0)|00:00:01|
|*2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX| 2 | | 1 (0)|00:00:01|
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SALARY">1000)
2 - access("DEPARTMENT_ID"=20)
 
8.3.3.4 Index Range Scan Descending: Example
이 예에서는 색인을 사용하여 employees 테이블에서 정렬 된 순서로 행을 검색합니다.
다음 명령.은 부서 20의 직원에 대한 레코드를 내림차순으로 조회합니다.
 
SELECT *
FROM employees
WHERE department_id < 20
ORDER BY department_id DESC;
 
이 선행 쿼리는 카디널리티가 낮으므로 쿼리는 department_id 열의 인덱스를 사용합니다.
 
SQL_ID 8182ndfj1ttj6, child number 0
-------------------------------------
SELECT * FROM employees WHERE department_id < 20 ORDER BY department_id DESC
Plan hash value: 1681890450
--------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time |
--------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 2 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 2|138| 2 (0)|00:00:01|
|*2| INDEX RANGE SCAN DESCENDING| EMP_DEPARTMENT_IX | 2| | 1 (0)|00:00:01|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"<20)
 
데이터베이스는 20 이하의 가장 높은 키 값을 포함하는 첫 번째 인덱스 리프 블록을 찾습니다.
그런 다음 스캔은 리프 노드의 링크 된 목록을 통해 왼쪽으로 수평으로 진행됩니다.
데이터베이스는 각 색인 항목에서 ROWID를 확보 한 다음 ROWID에 의해 지정된 행을 검색합니다.
 
8.3.4 Index Full Scans
색인 전체 스캔은 전체 색인을 순서대로 읽습니다.
인덱스 전체 스캔은 인덱스의 데이터가 인덱스 키순으로 정렬되기 때문에 별도의 정렬 작업을 제거 할 수 있습니다.
 
8.3.4.1 When the Optimizer Considers Index Full Scans
옵티마이 저는 다양한 상황에서 인덱스 전체 스캔을 고려합니다.
상황은 다음과 같습니다.
• 술어가 인덱스의 열을 참조합니다. 이 열은 선행 열 일 필요는 없습니다.
• 술어가 지정되지 않았지만 다음 조건이 모두 충족됩니다.
- 테이블 W 조회의 모든 열이 색인에 있습니다.
- 적어도 하나의 색인화 된 열이 널이 아닙니다.
• 질의에는 색인화 된 Null을 허용하지 않는 열에 ORDER BY가 포함됩니다.
 
8.3.4.2 How Index Full Scans Work
데이터베이스는 루트 블록을 읽은 다음 리프 블록에 도달 할 때까지 인덱스의 왼쪽을 탐색합니다 (내림차순 전체 스캔을 수행하는 경우 오른쪽).
그런 다음 데이터베이스가 리프 블록에 도달하면 정렬 순서대로 한 번에 한 블록 씩 색인 맨 아래로 스캔이 진행됩니다.
데이터베이스는 멀티 블록 I/O보다는 단일 블록 I/O를 사용합니다.
다음 그래픽은 인덱스 전체 스캔을 보여줍니다. 명령문은 department_id로 정렬 된 부서 레코드를 요청합니다.
 
Figure 8-6 Index Full Scan
 
8.3.4.3 Index Full Scans: Example
이 예에서는 인덱스 전체 검색을 사용하여 ORDER BY 절이있는 쿼리를 만족시킵니다.
다음 명령문은 부서 ID와 이름을 부서 ID 순으로 조회합니다.
 
SELECT department_id, department_name
FROM departments
ORDER BY department_id;
 
다음 계획은 옵티마이 저가 인덱스 전체 스캔을 선택했음을 보여줍니다.
 
SQL_ID 94t4a20h8what, child number 0
-------------------------------------
select department_id, department_name from departments order by department_id
Plan hash value: 4179022242
--------------------------------------------------------------------------------
|Id | Operation | Name |Rows|Bytes|Cost(%CPU)|Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 27 | 432 | 2 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | DEPT_ID_PK | 27 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------
 
데이터베이스는 첫 번째 인덱스 리프 블록을 찾은 다음 리프 노드의 링크 된 목록을 통해 오른쪽으로 수평으로 진행합니다.
각 인덱스 항목에 대해 데이터베이스는 항목에서 ROWID를 확보 한 다음 ROWID에 의해 지정된 테이블 행을 검색합니다.
인덱스가 department_id로 정렬되기 때문에 데이터베이스는 검색된 행을 정렬하기위한 별도의 조작을 피합니다.
 
8.3.5 Index Fast Full Scans
인덱스 빠른 전체 검색은 디스크에있는 정렬되지 않은 순서로 인덱스 블록을 읽습니다.
이 검색은 인덱스를 사용하여 테이블을 조사하지는 않지만 기본적으로 인덱스 자체를 테이블로 사용하여 테이블 대신 인덱스를 읽습니다.
 
8.3.5.1 When the Optimizer Considers Index Fast Full Scans
옵티마이 저는 u 리가 인덱스의 속성에만 액세스 할 때이 스캔을 고려합니다.
 
노트 :
전체 검사와 달리 빠른 전체 검사는 색인을 순서대로 읽지 않기 때문에 정렬 작업을 제거 할 수 없습니다.
INDEX_FFS (table_name index_name) 힌트는 빠른 전체 색인 스캔을 강제 실행합니다.
 
See Also: Oracle Database SQL Language Reference to learn more about the INDEX hint
 
8.3.5.2 How Index Fast Full Scans Work
데이터베이스는 다중 블록 I / O를 사용하여 루트 블록과 모든 리프 및 분기 블록을 읽습니다.
데이터베이스는 분기 및 루트 블록을 무시하고 리프 블록의 색인 항목을 읽습니다.
 
8.3.5.3 Index Fast Full Scans: Example
이 예제는 옵티 마이저 힌트의 결과로 빠른 전체 인덱스 스캔을 사용합니다.
다음 명령문은 부서 ID와 이름을 부서 ID 순으로 조회합니다.
 
SELECT /*+ INDEX_FFS(departments dept_id_pk) */ COUNT(*)
FROM departments;
 
다음 플랜은 옵티마이 저가 빠른 전체 인덱스 스캔을 선택했음을 보여줍니다.
 
SQL_ID fu0k5nvx7sftm, child number 0
-------------------------------------
select /*+ index_ffs(departments dept_id_pk) */ count(*from departments
Plan hash value: 3940160378
--------------------------------------------------------------------------
| Id | Operation | Name | Rows |Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| DEPT_ID_PK | 27 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
 
8.3.6 Index Skip Scans
인덱스 건너 뛰기 스캔은 복합 인덱스의 초기 열이 "생략"되거나 쿼리에서 지정되지 않은 경우 발생합니다.
 
See Also: Oracle Database Concepts
 
8.3.6.1 When the Optimizer Considers Index Skips Scans
종종 인덱스 블록 스킵은 테이블 블록 스캔보다 빠르며 전체 인덱스 스캔을 수행하는 것보다 빠릅니다.
다음 조건이 충족 될 때 최적화 프로그램은 건너 뛰기 검사를 고려합니다.
• 복합 인덱스의 선행 열은 쿼리 조건 자에 지정되어 있지 않습니다.
예를 들어, 쿼리 술어는 cust_gender 열을 참조하지 않고 복합 인덱스 키는 (cust_gender, cust_email)입니다.
• 복합 인덱스의 leading 열에는 별개의 값이 거의 없지만 인덱스의 논 리딩 키에는 많은 고유 한 값이 있습니다.
예를 들어, 복합 색인 키가 (cust_gender, cust_email)이면 cust_gender 열에는 두 개의 고유 한 값만 있지만 cust_email에는 수천 개의 값이 있습니다.
 
8.3.6.2 How Index Skip Scans Work
인덱스 스킵 스캔은 합성 인덱스를 논리적으로 작은 하위 인덱스로 논리적으로 분할합니다.
인덱스의 선행 C 럼에서 구별 값의 수는 논리적 서브 인덱스의 수를 결정합니다.
숫자가 낮을수록, 최적화 프로그램이 작성해야하는 논리적 서브 인덱스가 적어지고 스캔 효율이 높아집니다.
스캔은 각 논리적 색인을 개별적으로 읽고 비 선도 열의 필터 조건을 충족시키지 않는 색인 블록을 "건너 뜁니다".
 
8.3.6.3 Index Skip Scans: Example
이 예는 인덱스 스킵 스캔을 사용하여 customers 테이블의 쿼리를 충족시킵니다.
고객 테이블에는 값이 M 또는 F 인 cust_gender 열이 있습니다.
다음과 같이 열 (cust_gender, cust_email)에 복합 인덱스를 만듭니다.
 
CREATE INDEX cust_gender_email_ix
ON sh.customers (cust_gender, cust_email);
 
개념적으로, 색인의 일부는 다음과 같을 수 있습니다. M의 성별 값은 색인의 앞 가장자리입니다.
F,Wolf@company.example.com,rowid
F,Wolsey@company.example.com,rowid
F,Wood@company.example.com,rowid
F,Woodman@company.example.com,rowid
F,Yang@company.example.com,rowid
F,Zimmerman@company.example.com,rowid
M,Abbassi@company.example.com,rowid
M,Abbey@company.example.com,rowid
 
sh.customers 테이블에서 고객에 대해 다음 쿼리를 실행합니다.
 
SELECT *
FROM sh.customers
WHERE cust_email = 'Abbey@company.example.com';
 
cust_gender가 WHERE 절에 지정되지 않은 경우에도 데이터베이스에서 customers_gender_email 인덱스의 스킵 스캔을 사용할 수 있습니다.
샘플 색인에서 선행 열 cust_gender에는 F와 M의 두 가지 값이 있습니다.
데이터베이스는 논리적으로 인덱스를 두 개로 나눕니다.
하나의 하위 색인에는 다음 형식의 항목이 있는 키 F가 있습니다.
F,Wolf@company.example.com,rowid
F,Wolsey@company.example.com,rowid
F,Wood@company.example.com,rowid
F,Woodman@company.example.com,rowid
F,Yang@company.example.com,rowid
F,Zimmerman@company.example.com,rowid
 
두 번째 하위 색인에는 다음과 같은 형식의 항목이있는 키 M이 있습니다.
M,Abbassi@company.example.com,rowid
M,Abbey@company.example.com,rowid
 
전자 메일이 Abbey@company.com 인 고객에 대한 레코드를 검색 할 때 데이터베이스는 선행 값 F로 먼저 하위 인덱스를 검색 한 다음 선행 값 M으로 하위 인덱스를 검색합니다. 
개념적으로 데이터베이스는 다음과 같이 쿼리를 처리합니다.
(SELECT *
FROM sh.customers
WHERE cust_gender = 'F'
AND cust_email = 'Abbey@company.com' )
UNION ALL
(SELECT *
FROM sh.customers
WHERE cust_gender = 'M'
AND cust_email = 'Abbey@company.com' );
 
쿼리 계획은 다음과 같습니다.
SQL_ID d7a6xurcnx2dj, child number 0
-------------------------------------
SELECT * FROM sh.customers WHERE cust_email = 'Abbey@company.example.com'
Plan hash value: 797907791
-----------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time|
-----------------------------------------------------------------------------------------
| 0|SELECT STATEMENT | | | |10(100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS |33|6237| 10(0)|00:00:01|
|*2| INDEX SKIP SCAN | CUST_GENDER_EMAIL_IX |33| | 4(0)|00:00:01|
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CUST_EMAIL"='Abbey@company.example.com')
filter("CUST_EMAIL"='Abbey@company.example.com')
 
See Also: Oracle Database Concepts to learn more about skip scans
 
8.3.7 Index Join Scans
인덱스 조인 검색은 쿼리에서 요청한 모든 열을 함께 반환하는 여러 인덱스의 해시 조인입니다.
모든 데이터가 인덱스에서 검색되기 때문에 데이터베이스에 테이블을 액세스 할 필요가 없습니다.
 
8.3.7.1 When the Optimizer Considers Index Join Scans
경우에 따라 테이블 액세스를 피하는 것이 가장 비용 효율적인 옵션입니다.
옵티마이 저가 다음과 같은 경우 색인 결합을 고려합니다.
• 여러 인덱스의 해시 조인은 테이블 액세스가 필요없이 쿼리에서 요청한 모든 데이터를 검색합니다.
• 테이블에서 행을 검색하는 비용은 테이블에서 행을 검색하지 않고 인덱스를 읽는 것보다 높습니다.
색인 결합은 종종 비용이 많이 듭니다.
예를 들어 두 개의 인덱스를 스캔하여 결합 할 때 가장 선택적인 인덱스를 선택한 다음 테이블을 조사하는 것이 비용이 적게 듭니다.
INDEX_JOIN (table_name) 힌트를 사용하여 인덱스 조인을 지정할 수 있습니다.
 
See Also: Oracle Database SQL Language Reference
 
8.3.7.2 How Index Join Scans Work
인덱스 조인은 여러 인덱스를 스캔 한 다음이 스캔에서 얻은 ROWID에 대해 해시 조인을 사용하여 행을 반환합니다.
인덱스 조인 검색에서 테이블 액세스는 항상 방지됩니다.
예를 들어, 단일 테이블에서 두 개의 인덱스를 조인하는 프로세스는 다음과 같습니다.
1. 첫 번째 색인을 스캔하여 ROWID를 검색하십시오.
2. 두 번째 색인을 스캔하여 ROWID를 검색하십시오.
3. rowid에 의해 해시 조인을 수행하여 행을 확보하십시오.
 
8.3.7.3 Index Join Scans: Example
다음 성명은 성이 A로 시작하는 직원의 성 및 전자 메일을 쿼리하여 인덱스 조인을 지정합니다.
SELECT /*+ INDEX_JOIN(employees) */ last_name, email
FROM employees
WHERE last_name like 'A%';
 
별도의 색인이 (last_name, first_name) 및 email 열에 있습니다.
emp_name_ix 색인의 일부는 다음과 같습니다.
 
Banda,Amit,AAAVgdAALAAAABSABD
Bates,Elizabeth,AAAVgdAALAAAABSABI
Bell,Sarah,AAAVgdAALAAAABSABc
Bernstein,David,AAAVgdAALAAAABSAAz
Bissot,Laura,AAAVgdAALAAAABSAAd
Bloom,Harrison,AAAVgdAALAAAABSABF
Bull,Alexis,AAAVgdAALAAAABSABV
 
emp_email_uk 색인의 첫 번째 부분은 다음과 같습니다.
ABANDA,AAAVgdAALAAAABSABD
ABULL,AAAVgdAALAAAABSABV
ACABRIO,AAAVgdAALAAAABSABX
AERRAZUR,AAAVgdAALAAAABSAAv
AFRIPP,AAAVgdAALAAAABSAAV
AHUNOLD,AAAVgdAALAAAABSAAD
AHUTTON,AAAVgdAALAAAABSABL
 
예 8-4 (8-28 페이지)는 DBMS_XPLAN.DISPLAY_CURSOR 함수를 사용하여 계획을 검색합니다.
데이터베이스는 emp_email_uk 색인의 모든 ROWID를 검색 한 다음 A로 시작하는성에 대해 emp_name_ix의 ROWID를 검색합니다.
데이터베이스는 해시 조인을 사용하여 일치하는 두 ROWID 집합을 모두 검색합니다.
예를 들어, rowid AAAVgdAALAAAABSABD는 두 ROWID 세트에서 발생하므로 데이터베이스는이 ROWID에 해당하는 레코드에 대해 employees 테이블을 프로브합니다.
 
Example 8-4 Index Join Scan
SQL_ID d2djchyc9hmrz, child number 0
-------------------------------------
SELECT /*+ INDEX_JOIN(employees) */ last_name, email FROM employees
WHERE last_name like 'A%'
Plan hash value: 3719800892
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | VIEW | index$_join$_001 | 3 | 48 | 3 (34)| 00:00:01 |
|* 2 | HASH JOIN | | | | | |
|* 3 | INDEX RANGE SCAN | EMP_NAME_IX | 3 | 48 | 1 (0)| 00:00:01 |
| 4 | INDEX FAST FULL SCAN| EMP_EMAIL_UK | 3 | 48 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("LAST_NAME" LIKE 'A%')
2 - access(ROWID=ROWID)
3 - access("LAST_NAME" LIKE 'A%')
 
8.4 Bitmap Index Access Paths
비트 맵 인덱스는 인덱싱 된 데이터를 ROWID 범위와 결합합니다.
 
8.4.1 About Bitmap Index Access
일반적인 B- 트리 색인에서 하나의 색인 항목은 단일 행을 가리 킵니다.
비트 맵 인덱스에서 키는 인덱싱 된 데이터와 ROWID 범위의 조합입니다.
데이터베이스는 각 색인 키에 대해 적어도 하나의 비트 맵을 저장합니다. 비트 맵의 각 값은 일련의 1과 0 값으로, rowid 범위 내의 행을 가리 킵니다.
따라서 비트 맵 인덱스에서 하나의 인덱스 항목은 단일 행이 아닌 일련의 행을 가리 킵니다.
 
8.4.1.1 Differences Between Bitmap and B-Tree Indexes
비트 맵 인덱스는 B- 트리 인덱스의 다른 키를 사용하지만 B- 트리 구조에 저장됩니다.
다음 표에서는 색인 항목 유형 간의 차이점을 보여줍니다.
 
Table 8-2 Index Entries for B-Trees and Bitmaps
 
데이터베이스는 B- 트리 구조에 비트 맵 인덱스를 저장합니다.
데이터베이스는 인덱스가 정의 된 속성 세트 인 키의 첫 번째 부분에서 B- 트리를 신속하게 검색 한 다음 해당하는 rowid 범위와 비트 맵을 가져올 수 있습니다.
 
See Also:
• "Bitmap Storage (page 8-33)"
• Oracle Database Concepts for an overview of bitmap indexes
• Oracle Database Data Warehousing Guide for more information about bitmap indexes
 
8.4.1.2 Purpose of Bitmap Indexes
비트 맵 색인은 드물게 수정되는 하위 고유 카디널리티 데이터에 적합합니다.
열의 고유 값 수가 행 총 수와 관련하여 낮 으면 데이터의 카디널리티가 낮습니다.
일반적으로 B-tree 인덱스는 상위 카디널리티 데이터에 적합합니다.
옵티마이 저는 일부 행을 리턴하는 조회에 대해 B 트리 색인을 선택할 수 있습니다.
반대로 비트 맵 인덱스는 낮은 고유 카디널리티 데이터에 적합합니다.
customers.cust_gender 열은 별개의 값 (M 및 F)과 null 만 있기 때문에 비트 맵 인덱스의 후보입니다.
테이블에 1 억 개의 행이 있으면 모든 여성 고객에 대한 쿼리는 선택 사항이 아니므로 비트 맵 인덱스 액세스 후보가됩니다.
압축 기술을 통해 비트 맵 인덱스는 최소한의 I / O로 많은 ROWID를 생성 할 수 있습니다.
비트 맵 인덱스는 데이터웨어 하우스의 임시 쿼리 속도를 높이는 유용한 방법입니다.
특히 비트 맵 인덱스는 다음을 포함하는 쿼리에서 유용한 액세스 경로입니다.
WHERE 절의 다중 조건
테이블 자체가 액세스되기 전에 데이터베이스는 모든 조건이 아닌 일부 조건을 만족하는 행을 필터링합니다.
• 하위 카디널리티 열에 대한 AND, OR 및 NOT 연산
낮은 카디널리티 열에 비트 맵 인덱스를 결합하면 이러한 작업이보다 효율적으로 수행됩니다.
데이터베이스는 비트 맵 인덱스의 비트 맵을 매우 빠르게 병합 할 수 있습니다.
예를 들어 비트 맵 인덱스가 고객의 cust_gender 및 cust_marital_status 열에 있으면이 인덱스는 다음 쿼리의 성능을 크게 향상시킬 수 있습니다.
 
SELECT *
FROM customers
WHERE cust_gender = 'M'
AND cust_marital_status = 'single';
 
데이터베이스는 병합 된 비트 맵의 1 값을 효율적으로 ROWID로 변환 할 수 있습니다.
• COUNT 함수
데이터베이스는 테이블을 스캔 할 필요없이 비트 맵 인덱스를 스캔 할 수 있습니다.
• null 값을 선택하는 술어
B- 트리 색인과 달리 비트 맵 색인에는 널 (null)이 포함될 수 있습니다. 열의 Null 수를 계산하는 쿼리는 테이블을 검색하지 않고 비트 맵 인덱스를 사용할 수 있습니다.
비트 맵 인덱스는 DML이 많은 테이블에 유용하지 않습니다. 그 이유는 하나의 색인 키가 많은 행을 가리 키기 때문입니다.
세션이 인덱싱 된 데이터를 수정하면 데이터베이스는 비트 맵의 단일 비트를 잠글 수 없으며 데이터베이스는 비트 맵이 가리키는 행을 실제로 잠그는 전체 인덱스 항목을 잠급니다.
예를 들어 특정 고객의 결혼 상태가 독신에서 결혼으로 변경되면 데이터베이스는 비트 맵의 단일 색인 항목과 결혼 색인 항목에 독점적으로 액세스해야합니다.
이 두 값을 포함하는 행은 COMMIT까지 수정 될 수 없습니다.
 
See Also: Oracle Database SQL Language Reference to learn about the COUNT function
 
8.4.1.3 Bitmaps and Rowids
비트 맵의 ​​특정 값의 경우 행 값이 비트 맵 조건과 일치하면 값은 1이고 그렇지 않은 경우 값은 0입니다.
이 값을 기반으로 데이터베이스는 내부 알고리즘을 사용하여 비트 맵을 ROWID에 매핑합니다.
비트 맵 항목은 색인 값, ROWID 범위 (시작 및 끝 ROWID) 및 비트 맵을 포함합니다.
비트 맵의 ​​각 0 또는 1 값은 행 ID 범위에 대한 오프셋이며 행이없는 경우에도 테이블의 잠재적 행에 매핑됩니다.
블록의 가능한 행 수는 미리 결정되므로 데이터베이스는 범위 끝점을 사용하여 범위의 임의 행의 ROWID를 결정할 수 있습니다.
노트 :
하칸 팩터는 Oracle Database가 단일 블록에 저장할 수 있다고 가정하는 행 수를 제한하기 위해 비트 맵 인덱스 알고리즘에서 사용되는 최적화입니다.
인위적으로 행 수를 제한함으로써 데이터베이스는 비트 맵의 ​​크기를 줄입니다.
 
표 8-3 (8-31 페이지)에는 null 값을 허용하는 sh.customers.cust_marital_status 열에 대한 샘플 비트 맵의 ​​일부가 나와 있습니다.
실제 색인에는 12 개의 별개 값이 있습니다. 샘플에는 3 개만 표시됩니다 (null, married 및 single).
 
Table 8-3 Bitmap Index Entries
 
표 8-3 (8-31 페이지)에 표시된 것처럼 비트 맵 인덱스에는 B- 트리 인덱스와 달리 전체 값으로 구성된 키가 포함될 수 있습니다.
표 8-3 (8-31 페이지)에서 범위의 여섯 번째 행에 대한 null 값은 1이며, cust_marital_status 값은 범위의 여섯 번째 행에 대해 null입니다.
널 (NULL) 인덱싱은 일부 SQL 문 (예 : 집계 함수 COUNT가있는 조회)에 유용 할 수 있습니다.
 
See Also: Oracle Database Concepts to learn about rowid formats
 
8.4.1.4 Bitmap Join Indexes
비트 맵 조인 인덱스는 둘 이상의 테이블 조인을위한 비트 맵 인덱스입니다.
 
옵티마이 저는 비트 맵 조인 인덱스를 사용하여 계획 실행 중에 조인해야하는 데이터의 양을 줄이거 나 제거 할 수 있습니다.
비트 맵 조인 인덱스는 구체화 된 조인 뷰보다 저장 영역에서 훨씬 효율적입니다.
다음 예제에서는 sh.sales 및 sh.customers 테이블에 비트 맵 인덱스를 만듭니다.
 
CREATE BITMAP INDEX cust_sales_bji ON sales(c.cust_city)
FROM sales s, customers c
WHERE c.cust_id = s.cust_id LOCAL;
 
앞의 CREATE 문의 FROM 및 WHERE 절은 테이블 간의 조인 조건을 나타냅니다.
customers.cust_city 열은 인덱스 키입니다.
인덱스의 각 키 값은 customers 테이블에서 가능한 도시를 나타냅니다.
개념적으로 인덱스의 키 값은 각 키 값과 연관된 하나의 비트 맵과 함께 다음과 같이 보일 수 있습니다.
 
비트 맵의 각 비트는 판매 테이블의 한 행에 해당합니다.
Smithville 키에서 값 1은 판매 테이블의 첫 번째 행이 Smithville 고객에게 판매 된 제품에 해당 함을 의미하고 값 0은 두 번째 행이 Smithville 고객에게 판매되지 않은 제품에 해당 함을 의미합니다.
스미스 빌 고객에 대한 별도 판매량에 대한 다음 쿼리를 고려하십시오.
 
SELECT COUNT (*)
FROM sales s, customers c
WHERE c.cust_id = s.cust_id
AND c.cust_city = 'Smithville';
 
다음 계획은 데이터베이스가 스미스 빌 비트 맵을 읽어 스미스 빌 판매 (4 단계) 수를 도출함으로써 고객 테이블과 판매 테이블의 조인을 피하는 방법을 보여줍니다.
 
SQL_ID 57s100mh142wy, child number 0
-------------------------------------
SELECT COUNT (*FROM sales s, customers c WHERE c.cust_id = s.cust_id
AND c.cust_city = 'Smithville'
Plan hash value: 3663491772
------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost (%CPU)| Time|Pstart|Pstop|
------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |29 (100)| | | |
| 1| SORT AGGREGATE | | 1 | 5| | | | |
| 2| PARTITION RANGE ALL | | 1708|8540|29 (0)|00:00:01|1|28|
| 3| BITMAP CONVERSION COUNT | | 1708|8540|29 (0)|00:00:01| | |
|*4| BITMAP INDEX SINGLE VALUE|CUST_SALES_BJI| | | | |1|28|
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("S"."SYS_NC00008$"='Smithville')
 
See Also: Oracle Database Concepts to learn about the CREATE INDEX statement
 
8.4.1.5 Bitmap Storage
비트 맵 인덱스는 B- 트리와 마찬가지로 분기 블록과 리프 블록을 사용하여 B- 트리 구조에 있습니다.
예를 들어, customers.cust_marital_status 열에 12 개의 고유 한 값이 있으면 하나의 분기 블록이 결혼 된 키, rowid-range 및 single, rowid-range를 가리킬 수 있고 다른 분기 블록은 widowed, rowidrange 키를 가리킬 수 있습니다. .
또는 단일 분기 블록이 12 개의 고유 키를 모두 포함하는 리프 블록을 가리킬 수 있습니다.
각 인덱싱 된 열 값은 하나 이상의 비트 맵 조각을 가질 수 있습니다. 각 비트 맵 조각은 고유 한 행 ID 범위가 하나 이상의 범위에서 인접한 행 집합을 차지합니다.
데이터베이스는 비트 맵 조각을 사용하여 블록 크기에 비해 상대적으로 큰 색인 항목을 분할 할 수 있습니다.
예를 들어, 데이터베이스는 하나의 색인 항목을 세 부분으로 나눌 수 있습니다. 처음 두 부분은 동일한 범위의 개별 블록으로 나누고 마지막 부분은 다른 부분의 다른 부분으로 분리합니다.
공간을 절약하기 위해 Oracle Database는 0 값의 연속적인 범위를 압축 할 수 있습니다.
 
8.4.2 Bitmap Conversion to Rowid
비트 맵 변환은 비트 맵의 항목과 표의 행 사이를 변환합니다.
변환은 항목에서 행 (TO ROWID) 또는 행에서 항목 (FROM ROWID)로 이동할 수 있습니다.
 
8.4.2.1 When the Optimizer Chooses Bitmap Conversion to Rowid
옵티마이 저는 비트 맵 인덱스 항목을 사용하여 테이블에서 행을 검색 할 때마다 변환을 사용합니다.
 
8.4.2.2 How Bitmap Conversion to Rowid Works
개념적으로, 비트 맵은 테이블로 표현 될 수 있습니다.
예를 들어, 표 8-3 (8-31 페이지)은 고객 행 번호가 열이고 cust_marital_status 값이 행인 비트 맵을 표로 나타냅니다.
표 8-3 (8-31 페이지)의 각 필드는 값 1 또는 0을 가지며 행의 열 값을 나타냅니다.
개념적으로, 비트 맵 변환은 "비트 맵의 필드 F는 테이블의 M 번째 블록의 N 번째 행에 해당합니다" 또는 "테이블의 M 번째 블록의 N 번째 행은 비트 맵."
 
8.4.2.3 Bitmap Conversion to Rowid: Example
이 예에서, 옵티마이 저는 범위 술어를 사용하여 조회를 만족시키기 위해 비트 맵 변환 조작을 선택합니다.
sh.customers 테이블의 쿼리는 1918 년 이전에 태어난 모든 고객의 이름을 선택합니다.
 
SELECT cust_last_name, cust_first_name
FROM customers
WHERE cust_year_of_birth < 1918;
 
다음 계획은 데이터베이스가 범위 스캔을 사용하여 1918 (3 단계) 미만의 모든 키 값을 찾은 다음 비트 맵의 1 값을 ROWID (2 단계)로 변환 한 다음 ROWID를 사용하여 고객으로부터 행을 얻는 방법을 보여줍니다 표 (1 단계) :
---------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |421 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS |3604|68476|421 (1)| 00:00:01 |
| 2| BITMAP CONVERSION TO ROWIDS | | | | | |
|*3| BITMAP INDEX RANGE SCAN | CUSTOMERS_YOB_BIX| | | | |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CUST_YEAR_OF_BIRTH"<1918)
filter("CUST_YEAR_OF_BIRTH"<1918)
 
8.4.3 Bitmap Index Single Value
이 유형의 액세스 경로는 비트 맵 색인을 사용하여 단일 키 값을 찾습니다.
 
8.4.3.1 When the Optimizer Considers Bitmap Index Single Value
옵티마이 저는 술어에 항등 연산자가 들어있을 때이 액세스 경로를 고려합니다.
 
8.4.3.2 How Bitmap Index Single Value Works
이 쿼리는 단일 값을 포함하는 위치에 대해 단일 비트 맵을 검색합니다. 데이터베이스는 1 값을 ROWID로 변환 한 다음 ROWID를 사용하여 행을 찾습니다.
데이터베이스는 단일 비트 맵 만 처리하면됩니다. 예를 들어, 다음 표는 sh.customers.cust_marital_status 열에서 widowed 값에 대한 비트 맵 인덱스 (두 비트 맵 조각으로)를 나타냅니다.
위도 (widow)가있는 고객의 쿼리를 만족시키기 위해 데이터베이스는 사장 된 비트 맵의 각 1 값을 검색하고 해당 행의 ROWID를 찾을 수 있습니다.
Table 8-4 Bitmap Index Entries
 
8.4.3.3 Bitmap Index Single Value: Example
이 예에서, 옵티마이 저는 동등 술어를 사용하는 조회를 만족시키기 위해 비트 맵 색인 단일 값 조작을 선택합니다.
sh.customers 테이블의 쿼리는 모든 미망인 고객을 선택합니다.
 
SELECT *
FROM customers
WHERE cust_marital_status = 'Widowed';
 
다음 계획은 데이터베이스가 고객 비트 맵 색인에서 Widowed 키를 사용하여 항목을 읽고 (단계 3), 비트 맵의 1 값을 ROWID로 변환 한 다음 (단계 2), ROWID를 사용하여 고객의 행을 가져옵니다 표 (1 단계) :
 
SQL_ID ff5an2xsn086h, child number 0
-------------------------------------
SELECT * FROM customers WHERE cust_marital_status = 'Widowed'
Plan hash value: 2579015045
---------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost (%CPU)| Time|
---------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |412 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED|CUSTOMERS |3461|638K|412 (2)|00:00:01|
| 2| BITMAP CONVERSION TO ROWIDS | | | | | |
|*3| BITMAP INDEX SINGLE VALUE |CUSTOMERS_MARITAL_BIX| | | | |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CUST_MARITAL_STATUS"='Widowed')
 
8.4.4 Bitmap Index Range Scans
이 유형의 액세스 경로는 비트 맵 인덱스를 사용하여 값의 범위를 찾습니다.
 
8.4.4.1 When the Optimizer Considers Bitmap Index Range Scans
The optimizer considers this access path when the predicate selects a range of values.
The range in the scan can be bounded on both sides, or unbounded on one or both sides. 
The optimizer typically chooses a range scan for selective queries.
 
8.4.4.2 How Bitmap Index Range Scans Work
이 스캔은 B 트리 범위 스캔과 유사하게 작동합니다.
예를 들어, 다음 표는 sh.customers.cust_year_of_birth 열의 비트 맵 인덱스에있는 세 개의 값을 나타냅니다.
쿼리가 1917 이전에 태어난 모든 고객을 요청하면 데이터베이스는이 인덱스를 1917보다 낮은 값으로 검색 한 다음 1이있는 행에 대한 ROWID를 가져올 수 있습니다.
Table 8-5 Bitmap Index Entries
 
8.4.4.3 Bitmap Index Range Scans: Example
이 예에서는 범위 스캔을 사용하여 1 년 전에 태어난 고객을 선택합니다.
sh.customers 테이블의 쿼리는 1918 년 이전에 태어난 고객의 이름을 선택합니다.
 
SELECT cust_last_name, cust_first_name
FROM customers
WHERE cust_year_of_birth < 1918;
 
다음 계획은 데이터베이스가 1918 년 (단계 3)보다 낮은 cust_year_of_birth 키에 대한 모든 비트 맵을 가져오고, 비트 맵을 ROWID로 변환 한 다음 (단계 2) 행을 페치합니다 (1 단계).
SQL_ID 672z2h9rawyjg, child number 0
-------------------------------------
SELECT cust_last_name, cust_first_name FROM customers WHERE
cust_year_of_birth < 1918
Plan hash value: 4198466611
---------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time |
---------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |421 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS |3604|68476|421 (1)|00:00:01 |
| 2| BITMAP CONVERSION TO ROWIDS | | | | | |
|*3| BITMAP INDEX RANGE SCAN | CUSTOMERS_YOB_BIX | | | | |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CUST_YEAR_OF_BIRTH"<1918)
filter("CUST_YEAR_OF_BIRTH"<1918)
 
8.4.5 Bitmap Merge
이 액세스 경로는 여러 비트 맵을 병합하고 그 결과 단일 비트 맵을 리턴합니다.
비트 맵 병합은 실행 계획의 BITMAP MERGE 연산에 의해 표시됩니다.
 
8.4.5.1 When the Optimizer Considers Bitmap Merge
일반적으로 옵티마이 저는 비트 맵 병합을 사용하여 비트 맵 인덱스 범위 스캔에서 생성 된 비트 맵을 결합합니다.
 
8.4.5.2 How Bitmap Merge Works
병합은 두 비트 맵 간의 부울 OR 연산을 사용합니다.
결과 비트 맵은 첫 번째 비트 맵의 모든 행과 모든 후속 비트 맵의 모든 행을 선택합니다.
검색어는 1918 년 이전에 태어난 모든 고객을 선택할 수 있습니다.
다음 예는 1917 년, 1916 년 및 1915 년의 세 가지 customers.cust_year_of_birth 키의 샘플 비트 맵을 보여줍니다.
비트 맵의 모든 위치에 1이 있으면 병합 된 비트 맵의 위치는 1입니다.
그렇지 않으면 병합 된 비트 맵에 0이 있습니다.
결과 비트 맵의 1 값은 19151916 또는 1917 값을 포함하는 행에 해당합니다.
 
8.4.5.3 Bitmap Merge: Example
이 예에서는 데이터베이스가 범위 조건자를 사용하여 쿼리를 최적화하기 위해 비트 맵을 병합하는 방법을 보여줍니다.
sh.customers 테이블의 쿼리는 1918 년 이전에 태어난 여성 고객의 이름을 선택합니다.
 
SELECT cust_last_name, cust_first_name
FROM customers
WHERE cust_gender = 'F'
AND cust_year_of_birth < 1918;
 
다음 계획은 데이터베이스가 cust_year_of_birth 키가 1918 (6 단계) 미만인 모든 비트 맵을 얻은 다음 OR 논리를 사용하여 이러한 비트 맵을 병합하여 단일 비트 맵 (5 단계)을 만드는 것을 보여줍니다.
데이터베이스는 F의 cust_gender 키에 대해 단일 비트 맵을 얻은 다음 (4 단계)이 두 비트 맵에서 AND 연산을 수행합니다.
결과는 요청 된 행에 대해 하나의 값을 포함하는 단일 비트 맵입니다 (3 단계).
SQL_ID 1xf59h179zdg2, child number 0
-------------------------------------
select cust_last_name, cust_first_name 
from customers 
where cust_gender = 'F' and cust_year_of_birth < 1918;
 
Plan hash value: 49820847
---------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |288 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED|CUSTOMERS |1802|37842|288 (1)|00:00:01|
| 2| BITMAP CONVERSION TO ROWIDS | | | | | |
| 3| BITMAP AND | | | | | |
|*4| BITMAP INDEX SINGLE VALUE |CUSTOMERS_GENDER_BIX| | | | |
| 5| BITMAP MERGE | | | | | |
|*6| BITMAP INDEX RANGE SCAN |CUSTOMERS_YOB_BIX | | | | |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("CUST_GENDER"='F')
6 - access("CUST_YEAR_OF_BIRTH"<1918)
filter("CUST_YEAR_OF_BIRTH"<1918)
 
8.5 Table Cluster Access Paths
테이블 클러스터는 공통 컬럼을 공유하고 관련 데이터를 동일한 블록에 저장하는 테이블 그룹입니다.
테이블이 클러스터되면 단일 데이터 블록에 여러 테이블의 행이 포함될 수 있습니다.
 
See Also: Oracle Database Concepts for an overview of table clusters
 
8.5.1 Cluster Scans
인덱스 클러스터는 인덱스를 사용하여 데이터를 찾는 테이블 클러스터입니다.
클러스터 색인은 클러스터 키의 B- 트리 색인입니다.
클러스터 검색은 인덱싱 된 클러스터에 저장된 테이블의 동일한 클러스터 키 값을 가진 모든 행을 검색합니다.
 
8.5.1.1 When the Optimizer Considers Cluster Scans
데이터베이스는 쿼리가 인덱싱 된 클러스터의 테이블에 액세스 할 때 클러스터 검색을 고려합니다.
 
8.5.1.2 How a Cluster Scan Works
인덱스 된 클러스터에서 데이터베이스는 동일한 클러스터 키 값을 가진 모든 행을 동일한 데이터 블록에 저장합니다.
예를 들어, hr.employees2 및 hr.departments2 테이블이 emp_dept_cluster에 클러스터되어 있고 클러스터 키가 department_id 인 경우 데이터베이스는 부서 10의 모든 직원을 동일한 블록에 저장하고, 부서 20의 모든 직원을 동일한 블록에 저장하고, 등등.
B 트리 클러스터 인덱스는 클러스터 키 값을 데이터가 들어있는 블록의 데이터베이스 블록 주소 (DBA)와 연관시킵니다.
예를 들어, 키 30의 색인 항목은 부서 30의 직원에 대한 행을 포함하는 블록의 주소를 표시합니다.
 
30,AADAAAA9d
 
사용자가 클러스터의 행을 요청하면 데이터베이스는 인덱스를 스캔하여 행을 포함하는 블록의 DBA를 가져옵니다.
그런 다음 Oracle Database는 이러한 DBA를 기반으로 행을 찾습니다.
 
8.5.1.3 Cluster Scans: Example
이 예에서는 department_id 열의 employees 및 departments 테이블을 클러스터링 한 다음 클러스터에 단일 부서를 쿼리합니다.
hr 사용자로 다음과 같이 클러스터에 테이블 클러스터, 클러스터 인덱스 및 테이블을 만듭니다.
CREATE CLUSTER employees_departments_cluster
(department_id NUMBER(4)) SIZE 512;
 
CREATE INDEX idx_emp_dept_cluster
ON CLUSTER employees_departments_cluster;
 
CREATE TABLE employees2
CLUSTER employees_departments_cluster (department_id)
AS SELECT * FROM employees;
 
CREATE TABLE departments2
CLUSTER employees_departments_cluster (department_id)
AS SELECT * FROM departments;
 
다음과 같이 부서 30의 직원을 쿼리합니다.
 
SELECT *
FROM employees2
WHERE department_id = 30;
 
스캔을 수행하기 위해 Oracle Database는 먼저 클러스터 색인을 스캔하여 부서 30을 설명하는 행의 rowid를 얻습니다 (2 단계).
그런 다음 Oracle Database는이 rowid를 사용하여 employees2의 행을 찾습니다 (1 단계).
SQL_ID b7xk1jzuwdc6t, child number 0
-------------------------------------
SELECT * FROM employees2 WHERE department_id = 30
Plan hash value: 49826199
--------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)| Time |
--------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 2 (100)| |
| 1| TABLE ACCESS CLUSTER| EMPLOYEES2 | 6 | 798 | 2 (0)| 00:00:01|
|*2| INDEX UNIQUE SCAN |IDX_EMP_DEPT_CLUSTER| 1 | | 1 (0)| 00:00:01|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=30)
 
See Also: Oracle Database Concepts to learn about indexed clusters
 
8.5.2 Hash Scans
해시 클러스터는 인덱스 키가 해시 함수로 대체된다는 점을 제외하면 인덱싱 된 클러스터와 같습니다.
별도의 클러스터 인덱스가 없습니다. 해시 클러스터에서 데이터는 색인입니다.
데이터베이스는 해시 검색을 사용하여 해시 값을 기반으로 해시 클러스터의 행을 찾습니다.
 
8.5.2.1 When the Optimizer Considers a Hash Scan
데이터베이스는 쿼리가 해시 클러스터의 테이블에 액세스 할 때 해시 검색을 고려합니다.
 
8.5.2.2 How a Hash Scan Works
해시 클러스터에서 동일한 해시 값을 가진 모든 행은 동일한 데이터 블록에 저장됩니다.
클러스터의 해시 검색을 수행하기 위해 Oracle Database는 먼저 명령문에 지정된 클러스터 키 값에 해시 함수를 적용하여 해시 값을 얻습니다.
그런 다음 Oracle Database는이 해시 값을 가진 행을 포함하는 데이터 블록을 스캔합니다.
 
8.5.2.3 Hash Scans: Example
이 예에서는 department_id 열에서 employees 및 departments 테이블을 해시 한 다음 클러스터에서 단일 부서를 쿼리합니다.
다음과 같이 해시 클러스터 및 테이블을 클러스터에 만듭니다.
CREATE CLUSTER employees_departments_cluster
(department_id NUMBER(4)) SIZE 8192 HASHKEYS 100;
 
CREATE TABLE employees2
CLUSTER employees_departments_cluster (department_id)
AS SELECT * FROM employees;
 
CREATE TABLE departments2
CLUSTER employees_departments_cluster (department_id)
AS SELECT * FROM departments;
 
다음과 같이 부서 30의 직원을 쿼리합니다.
 
SELECT *
FROM employees2
WHERE department_id = 30;
 
해시 검색을 수행하기 위해 Oracle Database는 먼저 해시 함수를 키 값 30에 적용하여 해시 값을 얻은 다음이 해시 값을 사용하여 데이터 블록을 스캔하고 행을 검색합니다 (1 단계).
SQL_ID 919x7hyyxr6p4, child number 0
-------------------------------------
SELECT * FROM employees2 WHERE department_id = 30
Plan hash value: 2399378016
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 |
|* 1 | TABLE ACCESS HASH| EMPLOYEES2 | 10 | 1330 | |
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("DEPARTMENT_ID"=30)
 
See Also: Oracle Database Concepts to learn about hash clusters
cs