====== 3.2.4. 비트맵(Bitmap) 실행계획 ====== # 조건연산자는 단지 비트연산의 방식만 다르게 할 뿐이기 때문에 조건연산자의 형태에 따라 처리방법이 크게 달라지지 않는다. * 조건 연산자별 비트맵 실행계획 * 동치(Equal) 비교 실행계획 * 범위(Range) 비교 실행계획 * AND 조건 실행계획 * OR 조건 실행계획 * 부등식(Not equal)실행계획 * NULL 비교 실행계획 * 서브 쿼리 실행계획 * B-Tree 인덱스와의 연합(Comnbine) 실행계획 ===== 3.2.4.1. 조건 연산자별 비트맵 실행계획 ===== {{http://download.oracle.com/docs/cd/B28359_01/server.111/b28313/dwhsg093.gif}} ===가) 동치(Equal) 비교 실행계획=== * '=' 이나 'IN' 연산 모두 SINGLE VALUE로 나타남 SELECT * FROM SALES_SUM WHERE TIME_CD='200512'; Execution Plan --------------------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX(SINGLE VALUE) OF 'TIME_BIX' .... WHERE TIME_CD IN ('200505','200507','200510'); Execution Plan ------------------------------------- INLIST ITERATOR TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX(SINGLE VALUE) OF 'TIME_BIX' ===나) 범위(Range)비교 실행계획=== * 'BETWEEN,LIKE,>,<,>=,<=' ; RANGE SCAN SELECT * FROM SALES_SUM WHERE TIME_CD BETWEEN '200505' AND '200507' Exectuion Plan ------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX (RANGE SCAN) OF 'TIME_BIX' * 만약 NUMBER 형으로 저장된 컬럼 LIKE 를 사용할 경우 * BTREE INDEX는 자료 변형으로 사용되지 않지만 BITMAP은 'FULL SCAN'으로 나타난다. SELECT * FROM FROM SALES_SUM WHERE SALE_DEPT LIKE '1250%' Execution Plan ------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX (FRULL SCAN) OF 'SALE_DEPT_BIX' ===다) AND 조건 실행계획=== * 두 비트맵 인덱스 보유 컬럼의 'AND' 조건 사용시 'AND 연산' 실시 SELECT * FROM SALES_SUM WHERE SALE_DEPT = '12500' AND TIME_CD ='200510'; Execution Plan ------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP AND BITMAP INDEX (SINGLE VALUE) OF 'TIME_BIX' BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' ===라) BETWEEN등으로 범위스캔하고 AND 조건 사용시=== SELECT * FROM SALES_SUM WHERE SALE_DEPT = '12500' AND TIME_CD BETWEEN '200510' AND '200512'; Execution Plan ------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP AND BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' BITMAP MERGE BITMAP INDEX (RANGE SCAN) OF 'TIME_BIX' ===마) OR조건 실행계획=== * 각컬럼별로 단위 액세스를 생성후 'OR' 연산 실시 SELECT * FROM SALES_SUM WHERE TIME_CD = '200510' OR ITEM_CD LIKE 'ABC%' OR SALE_DEPT = 12500; Execution Plan --------------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP OR BITMAP INDEX (SINGLE VALUE) OF 'TIME_BIX' BITMAP MERGE BITMAP INDEX (RANGE SCAN) OF 'ITEM_BIX' BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' * 복잡한 OR 조건에도 동일한 원리로 수행 * 부정형 조건과 사용되면 부정형 조건이 적용된 컬럼은 사용되지 않음 ===바) 부등식(Not equal)비교 실행계획=== * 'BITMAP MINUS' 연산을 수행 ; 등식 조건으로 액스사한 후 먼저 수행한 비트맵에서 제거하는 처리 * 단일 컬럼만 존재할경우 ; FULL SCAN 수행 * NOT NULL 제약 조건을 가진경우 AND 연산 ; BITMAP MINUS를 한번만 거침 SELECT * FROM SALES_SUM WHERE TIME_CD = '200512' AND SALE_DEPT <> 12500; Execution Plan ------------------------------- SELECT STATMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP MINUS BITMAP INDEX (SINGLE VALUE) OF 'TIME_BIX' BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' ===사) NULL을 허용할 경우=== BITMAP MINUS BITMAP MINUS BITMAP INDEX (SINGLE VALUE) OF 'TIME_BIX' BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' BITMAP INDEX (SINGLE VALUE) OF 'SALE_DEPT_BIX' <--- NULL인경우 제거 ===아) NULL비교 실행계획=== * NULL 연산 역시 일반 값들과 동일하게 비트맵 연산에 참여시켜 동작 SELECT * FROM SALES_SUM WHERE TIME_CD='200512' AND COUNTRY IS NULL; Execution Plan ---------------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' BITMAP CONVERSION (TO ROWIDS) BITMAP AND BITMAP INDEX (SINGLE VALUE) OF 'TIME_BIX' BITMAP INDEX (SINGLE VALUE) OF 'COUNTRY_BIX' ===== 3.2.4.2. 서브쿼리 실행계획 ===== SELECT * FROM SALES_SUM WHERE ITEM_CD IN (SELECT ITEM_CD FROM ITEM_T WHERE CATEGORY_CD = 'ABC' ); Execution Plan ------------------------------------ SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' NESTED LOOPS SORT (UNIQUE) <----- 서브 쿼리 TABLE ACCESS (BY INDEX ROWID) OF 'ITEM_T' INDEX (RANGE SCAN) OF 'ITEM_IDX2' (NON-UNIQUE) BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX (SINGLE VALUE) OF 'ITEM_BIX' <-- 제공 받은 값으로 비트맵 인덱스 ===가) 서브쿼리가 동시에 사용=== * 첫 서브쿼리가 먼저 수행되어 비트맵 인덱스로 실행되어 제공자 역할을 한후 후 수행되는 서브쿼리가 * 제공자 역할을 하는 형태의 수행계획이 생성됨 *스타 변형 조인 ; 모든 서브쿼리가 먼저 수행되어 처리 범위를 충분히 줄인 다음 팩트 테이블을 액세스 하는것 SELECT /*+ STAR_TRANSFORMATION */ I.ITEM_CD, SUM(S.AMOUNT) SAL_AMOUNTS FROM SALES_SUM S,ITEMS I,COUNTRYS C WHERE S.ITEM_CD = I.ITEM_CD AND S.COUNTRY = C.COUNTRY_CD AND I.CATEGORY_TYPE = 'Clothes' AND C.AREA='EUROPE' GROUP BY I.ITEM_CD; Execution Plan -------------------------------- SELECT STATMENT Optimizer=ALL_ROWS SORT GROUP BY HASH JOIN HASH JOIN <-- 엑세스한 팩트 테이블과 디멘전 테이블 조인 TABLE ACCESS (BY INDEX ROWID) OF 'SALES_SUM' <-- 비트맵들을 결합하여 액세스 BITMAP CONVERSION (TO ROWIDS) BITMAP AND BITMAP AND BITMAP MERGE <-- ITEMS 서브쿼리로 팩트 테입ㄹ의 비트맵 액세스 BITMAP KEY ITERATION TABLE ACCESS (FULL) OF 'ITEMS' BITMAP INDEX (RANGE SCAN) OF 'ITEM_BIX' BITMAP MERGE <-- COUNTRY 서브쿼리로 팩트 테이블의 비트맵 액세스 BITMAP KEY ITERATION TABLE ACCESS (FULL) OF 'COUNTRYS' BITMAP INDEX (RANGE SCAN) OF 'COUNTRY_BIX' ===== 3.2.4.3. B-tree 인덱스와의 연합(Combine) 실행계획 ===== * 'ITEM_CD'; 비트맵 인덱스 * 'PRICE' ; B-Tree 인덱스 SELECT /*+ INDEX_COMBINE (SALES) */ * FROM SALES WHERE ITEM_CD = 'PA100' AND PRICE >= 100000; Execution Plan ---------------------------------------- SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) OF 'SALES' BITMAP CONVERSION (TO ROWIDS) BITMAP AND BITMAP INDEX (SINGLE VALUE) OF 'ITEM_BIX' BITMAP CONVERSION (FROM ROWIDS) <- BTREE를 비트맵으로 전환 SORT (ORDER BY) INDEX (RANGE SCAN) OF 'PRICE_IDX' (NON-UNIQUE) * B-TREE인덱스만 사용될경우도 'INDEX_COMBINE' 힌트를 적용하면 비트맵 액세스가능 * 테이블 액세스전 최대한 인덱스간의 머지를 통해 범위를 줄이는것이 가능하지만 * 인덱스 머지의 오버해드가 더 커다면 역효과가 나타날수도 있음 ====== 3.2.5 기타 특수한 목적을 처리하는 실행계획 ====== ===== 3.2.5.1. 순환(Recursive) 실행계획 ===== {{http://pds11.egloos.com/pds/200905/11/61/b0064561_4a07c3cc81d87.gif}} * 'CONNECT BY ... START WITH' 문을 사용했을 때나타나는 실행계획 * 순전개 SELECT LPAD(' ',2* (LEVEL -1__ || ename, empno, sal,mgr, SYS_CONNECT_BY_PATH(last_name,'/') "Path" FROM emp WHERE job='CLERK' CONNECT BY mgr= PRIOR empno START WITH empno= :b1; Execution Plan ------------------------------------- SELECT STATEMENT FILETER --- 3 where절 job='clerk' CONNECT BY (WITH FILTERNING) NESTED LOOPS --- 1 start with 에서 정한 초기 조건 INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE) TABLE ACCESS (BY USER ROWID) OF 'EMP NESTED LOOPS ---2 connect by 절의 prior 절뒤쪽을 가져와서 상대 컬럼을 조인비교 BUFFER (SORT) CONNECT BY PUMY TABLE ACCESS (BY INDEX ROWID) OF 'EMP' INDEX (RANGE SCAN) OF 'MGR_IDX' (UNIQUE) * 각 단계(level)별 정렬이 필요할경우 ORDER SIBLINGS BY ... 을 사용 * connect by 의 연결에 사용할 인덱스가 없거나 테이블 크기가 적다면 해쉬조인으로 내측 루프를 수행 * 조인쿼리를 순환 전개 시킬경우 * where 절의 조인을 수행한후 루트들을 선별 (조인한 결과 집합이 필요하기 때문) ===== 3.2.5.2. UPDATE 서브쿼리 실행계획 ===== * UPDATE 문은 SET절과 WHERE 절에서 서브쿼리 사용가능 * SET 절의 서브쿼리를 스칼라 서브 쿼리 (하나의 로우에 하나의 컬럼값을 가짐) UPDATE emp e SET sal = (SELECT AVG(sal) * 1.2 FROM bouns b WHERE b.empno=e.empno AND b.pay_date between :b1 and :b2) WHERE deptno IN (SELECT deptno FROM dept WHERE loc='BOSTON'); Execution Plan ------------------------- UPDATE STATEMENT UPDATE OF 'EMP' TABLE ACCESS (BY INDEX ROWID) OF 'EMP' NESTED LOOPS TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' --- 서브쿼리를 수행 하여 메인쿼리와 조인 INDEX (RANGE SCAN) OF 'LOC_IDX' (NON-UNIQUE) INDEX (RANGE SCAN) OF 'EMP_DEPTNO_IDX' (NON-UNIQUE) SORT (AGGREGATE) TABLE ACCESS (BY INDEX ROWID) OF 'BONUS' -- SET 절의 스칼라 쿼리 수행 INDEX (RANGE SCAN) OF 'BONUS_PK' (UNIQUE) * UPDATE 문이라고해도 해쉬조인으로도 수행 * 메인쿼리에서 제공받은 값으로 수행된 서브쿼리의 결과가 'No DATA FOUND' 라면 'NULL'로 인정되어 갱신 * 에러: 쿼리가 정상적으로 수행되지 않은것 * 실패: 결과가 존재하지 않은것 ; NVL 등이 듣지 않음 * SUM,MIN,MAX,COUNT,AVG등의 그룹함수는 '실패'가 발생하지 않음 ===나) 뷰의 UPDATE CREATE OR REPLACE VIEW EMP_DEPT_VIEW AS SELECT X.EMPNO, X.ENAME, X.JOB, X.SAL, Y.LOC, Y.DNAME FROM EMP X, DEPT Y WHERE X.DEPTNO = Y.DEPTNO; UPDATE EMP_DEPT_VIEW E SET SAL= DECODE(DNAME,'SALES',1.2,1,1) * SAL WHERE LOC LIKE 'AB%'; Execution Plan ------------------------------------- UPDATE STATEMENT UPDATE OF 'EMP' HASH JOIN TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' INDEX (RANGE SCAN) OF 'LOC_IDX' (NON-UNIQUE) TABLE ACCESS (FULL) OF 'EMP' ===== 3.2.5.3. 특이한 형태의 실행계획 ===== 서브쿼리 팩토링 실행계획 ===가) 'WITH' : 쿼리를 임시 테이블에 저장한후 사용 (INLINE 뷰와 유사하게 사용)=== WITH total_sal AS (SELECT D.deptno, D.loc, E.job, sum(E.sal) tot_sal FROM emp E, dept D WHERE E.deptno=D.deptno AND E.hiredate > :b1 GROUP BY D.deptno, D.loc, E.job) SELECT e.empno, e.name, e.sal, e.sal/t.tot_sal sal_percent FROM emp e,total_sal t WHERE e.deptno = t.deptno AND e.sal > (SELECT max(tot_sal) FROM total_sal WHERE job='CLERK'); Execution Plan ---------------------- SELECT STATEMENT RECURSIVE EXECUTION OF 'SYS_LE_2_0' --- WITH 절의 쿼리 수행 TEMP TABLE TRANSFORMATION TABLE ACCESS (BY INDEX ROWID) OF OF 'EMP' NESTED LOOPS VIEW TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6616_165207A' INDEX (RANGE SCAN) OF 'EMP_DEPTNO_IDX' (NON-UNIQUE) SORT (AGGREGATE) VIEW TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6616_165207A' * TEMP TABLE TRANSFORMATION ; 기존의 쿼리를 새로 생성된 임시 테이블로 변형 * WITH절의 쿼리가 한번만 쓰임 인라인 뷰였다면 동일 쿼리를 두번수행 * 한번의 복잡한 가공을 여러번 재활용 할때 최적 * WITH절의 하나이상을 사용가능 ===나) 특인한 DELETE 문 서브쿼리=== DELETE FROM (SELECT * FROM EMP WHERE JOB='CLERK' AND comm > 10000 AND deptno IN (SELECT deptno FROM dept WHERE loc='BOSTOM')); Execution Plan ----------------------------------------- DELETE STATEMENT DELETE OF 'EMP' NESTED LOOPS TABLE ACCESS (BY INDEX ROWID) OF 'EMP' INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE) TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE) ===다) 다중테이블 입력(Multi-table Insert)서브쿼리=== INSERT ALL WHEN ORDER_TOTAL < 1000000 THEN INTO SMALL_ORDERS WHEN ORDER_TOTAL > 1000000 AND ORDER_TOTAL < 2000000 THEN INTO MEDIUM_ORDERS WHEN ORDER-TOTAL > 2000000 THEN INTO LARGE_ORDERS SELECT ORDER_ID,ORDER_TOTAL,SALES_REP_ID,CUSTOMER_ID FROM ORDERS; ------------------ INSERT STATEMENT MULTI-TABLE INSERT INTO OF 'SMALL_ORDERS' INTO OF 'MEDIUM_ORDERS' INTO OF 'LARGE_ORDERS' TABLE ACCESS (FULL) OF 'ORDERS' * 시스템 데이터를 관계형 데이터베이스 구조로 이행할때 유용 ===라) HAVING 절 서브쿼리 실행 계획=== SELECT department_id,manager_id FROM employees GROUP BY department_id,manager_id HAVING (department_id, manager_id) IN (SELECT e.deptno, e.mgr FROM emp e,dept d WHERE e.deptno = d.deptno AND d.oc='BOSTON'); Execution Plan ------------------------------- SELECT STATEMENT FILTER SORT (GROUP BY) TABLE ACCESS (FULL) OF 'EMPLOYEES' NESTED LOOPS TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' INDEX (RANGE SCAN) OF 'LOC_IDX' (NON-UNIQUE) AND-EQUAL INDEX (RANGE SCAN) OF 'EMP_MGR_IDX' (NON-UNIQUE) INDEX (RANGE SCAN) OF 'EMP_DEPTNO_IDX' (NON-UNIQUE) ===마) ROLLUP,CUBE,GROUPING SETS 처리 실행계획=== SELECT co.country_region,co.country_subregion, SUM*s.amount_sold) "Revenue", GROUP_ID() g FROM sales s,customers c, countries co WHERE s.cust_id=c.cust_id AND c.country_id=co.country_id AND s.time_id= :b1 AND co.country_region IN ('Americas','Europe') GROUP BY ROLLUP (co.country_region, co.country_subregion); Execution Plan ----------------------------- SELECT STATEMENT SORT (GROUP BY ROLLUP) NESTED LOOPS NESTED LOOPS PARTITION RANGE (SINGLE) TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'SALES' BITMAP CONVERSION (TO ROWIDS) BITMAP INDEX (SINGLE VALUE) OF 'SALES_TIME_BIX' TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS' INDEX (UNIQUE SCAN) OF 'CUSTOMERS_PK' (UNIQUE) INDEX (UNIQUE SCAN) OF 'COUNTRY_C_ID_PK' (UNIQUE) * CUBE 역시 동일 형태의 실행계획 생성 * GROUPING SETS; GROUPING SETS( (DEPTNO,JON),(ENAME,DEPTNO) ) 의 형태로 사용되며 * 정의된 그룹별로 결과가 나타남 ===바) MERGE문 실행계획=== * 조인에 성관한것은 UPDATE, 실패한것은 INSERT MERGE INTO bonuses D USING ( SELECT employee_id, salary, department_id FROM employees WHERE department_id=80) S ON (D.employee_id = S.employee_id) WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary * .01 DELETE WHERE (S.salary > 8000) WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus) VALUES (S.employee_id, S.salary * 0.1); Execution Plan ------------------------------------- MERGE STATEMENT MERGE OF 'BONUSES' VIEW NESTED LOOPS (OUTER) TABLE ACCESS (BY INDEX ROWID) OF 'EMPLOYEES' INDEX (RANGE SCAN) OF 'EMPLOYEE_X1' (NON-UNIQUE) TABLE ACCESS (BY INDEX ROWID) OF 'BONUSES' INDEX (RANGE SCAN) OF 'BONUSES_X1' (NON-UNIQUE)