728x90
    
    
  반응형
    
    
    
  
1. 힌트 작성 규칙
- 위치: SELECT, UPDATE, DELETE 키워드 바로 뒤
 - Query Block 당 하나의 주석에 묶어야 함
 - 별칭/원명 일관성: 테이블에 별칭을 썼으면 힌트도 별칭으로
 - Compound Query(UNION, UNION ALL): 각 SELECT 블록마다 지정
 - INSERT 전용 힌트: APPEND
 - 원칙: 가급적 힌트 대신 SQL/통계/인덱스로 해결
 - 부모/서브쿼리 위치 예시
 
-- 부모 블록에 힌트
SELECT /*+ ORDERED USE_NL(e d) */
       e.ename, d.dname
FROM   ( SELECT * FROM emp ) e
JOIN   dept d ON d.deptno = e.deptno;
-- 서브쿼리 블록에 힌트
SELECT e.ename
FROM   ( SELECT /*+ INDEX(emp emp_idx_deptno) */
                ename, deptno
         FROM   emp
         WHERE  deptno = :p_deptno
       ) e
JOIN   dept d ON d.deptno = e.deptno;
2. 최적화 방향 힌트
RULE
- 목적: Rule-Based Optimizer(RBO) 사용
 - 상황: 11g 이전 버전 호환
 - 효과: 통계 무시, 규칙 기반 실행계획
 
SELECT /*+ RULE */ ename FROM emp;
CHOOSE
- 목적: 통계 존재 여부에 따라 RBO/CBO 자동 선택
 - 효과: 사실상 의미 없음 (현행 DB는 CBO만 사용)
 
SELECT /*+ CHOOSE */ ename FROM emp;
FIRST_ROWS / FIRST_ROWS(n)
- 목적: 첫 행 또는 n행까지 빠른 응답 속도
 - 상황: OLTP 화면, 페이징 UI
 - 효과: 옵티마이저가 NL Join/인덱스 선호
 - 주의: 집계, GROUP BY, DISTINCT 등에서는 무시됨
 
SELECT /*+ FIRST_ROWS */ * FROM emp WHERE deptno = 10;
SELECT /*+ FIRST_ROWS(10) */ * FROM emp ORDER BY hiredate;
ALL_ROWS
- 목적: 전체 처리량 최소화 (CBO 기본)
 - 상황: 배치, 리포트
 - 효과: Hash Join, 병렬 처리 등 선택
 
SELECT /*+ ALL_ROWS */ deptno, COUNT(*) FROM emp GROUP BY deptno;
3. 조인 힌트
ORDERED
- 목적: FROM 절 순서대로 조인
 
SELECT /*+ ORDERED */ e.ename, d.dname
FROM dept d, emp e
WHERE d.deptno = e.deptno;
LEADING
- 목적: 드라이빙 테이블 지정
 
SELECT /*+ LEADING(e) */ e.ename, d.dname
FROM emp e JOIN dept d ON e.deptno = d.deptno;
USE_NL / USE_MERGE / USE_HASH
- 목적: 조인 방식을 강제
 - 효과:
- USE_NL: 소량 데이터 + 인덱스 → 응답 빠름
 - USE_MERGE: 대량 + 정렬 → 정렬 병합 조인
 - USE_HASH: 대량 + 비정렬 → 해시 조인
 
 
SELECT /*+ ORDERED USE_NL(e d) */ ...
SELECT /*+ ORDERED USE_MERGE(e d) */ ...
SELECT /*+ ORDERED USE_HASH(e d) */ ...
실행계획 비교 예시
SELECT /*+ USE_NL(e d) */ ...
SELECT /*+ USE_HASH(e d) */ ...
USE_NL (샘플)
| Id  | Operation             | Name | A-Rows | Buffers |
|  1  | NESTED LOOPS          |      |      1 |       5 |
|  2  |  INDEX RANGE SCAN     | EMPNO|      1 |       2 |
|  3  |  TABLE ACCESS FULL    | DEPT |      1 |       2 |
USE_HASH (샘플)
| Id  | Operation           | Name | A-Rows | Buffers |
|  1  | HASH JOIN           |      |      1 |       6 |
|  2  |  TABLE ACCESS FULL  | EMP  |      1 |       3 |
|  3  |  TABLE ACCESS FULL  | DEPT |      1 |       3 |
👉 소량일 때는 NL, 대량일 때는 HASH가 유리.
USE_NL_WITH_INDEX
- 목적: NL Join 시 인덱스 강제
 
SELECT /*+ USE_NL_WITH_INDEX(l item_product_ix) */ ...
STAR / STAR_TRANSFORMATION
- 목적: Star Schema 최적화
 - 효과: Dimension 필터를 Fact 조인에 반영 → 성능 개선
 
SELECT /*+ STAR */ ...
SELECT /*+ STAR_TRANSFORMATION */ ...
4. 접근 방법 힌트
- FULL: Full Table Scan 강제
 - ROWID: Rowid 접근
 - CLUSTER/HASH: 클러스터/해시 클러스터 스캔
 
SELECT /*+ FULL(emp) */ * FROM emp;
SELECT /*+ ROWID(emp) */ * FROM emp WHERE rowid = :rid;
SELECT /*+ CLUSTER(emp) */ * FROM emp;
SELECT /*+ HASH(emp) */ * FROM emp WHERE deptno = :p_deptno;
5. 인덱스 힌트
- INDEX/INDEX_ASC/INDEX_DESC: 인덱스 강제, 정렬 방향 지정
 - NO_INDEX: 특정 인덱스 배제
 - INDEX_COMBINE: 다중 Bitmap 인덱스 결합
 - INDEX_FFS: Index Fast Full Scan 강제
 - INDEX_JOIN: 여러 인덱스 조합
 - INDEX_SS: Skip Scan 강제
 
SELECT /*+ INDEX(emp emp_idx_deptno) */ * FROM emp WHERE deptno = 10;
SELECT /*+ NO_INDEX(emp emp_idx_salary) */ * FROM emp WHERE sal > 3000;
SELECT /*+ INDEX_FFS(emp emp_idx) */ COUNT(*) FROM emp;
SELECT /*+ INDEX_SS(emp emp_idx) */ * FROM emp WHERE job='CLERK';
👉 인덱스 여러 개 지정 → 비용 가장 낮은 것 선택
👉 인덱스 생략 → 사용 가능한 것 중 최소 비용 선택
6. 서브쿼리 힌트
- HASH_AJ/MERGE_AJ: NOT IN → Anti-Join 변환
 - HASH_SJ/MERGE_SJ: EXISTS → Semi-Join 변환
 - PUSH_SUBQ: 서브쿼리 조기 실행
 
SELECT e.* FROM emp e
WHERE e.deptno NOT IN (
  SELECT /*+ HASH_AJ */ d.deptno FROM dept d WHERE d.loc = 'DALLAS');
SELECT d.* FROM dept d
WHERE EXISTS (
  SELECT /*+ HASH_SJ */ 1 FROM emp e WHERE e.deptno = d.deptno);
7. 병렬 / DML / 모니터링 힌트
- PARALLEL: 병렬 실행
 - APPEND: Direct-Path Insert
 - MONITOR: SQL Monitoring 강제
 
SELECT /*+ PARALLEL(emp 4) FULL(emp) */ * FROM emp;
INSERT /*+ APPEND */ INTO sales_archive
SELECT * FROM sales WHERE txn_dt < DATE '2025-01-01';
SELECT /*+ MONITOR gather_plan_statistics */ *
FROM scott.dept d, scott.emp e
WHERE d.deptno = e.deptno AND d.deptno = 10;
8. 글로벌 vs 로컬 힌트
- 로컬: 현재 블록만 적용
 - 글로벌: View 내부까지 적용 (뷰별칭.내부별칭)
 
CREATE VIEW instructors AS
SELECT * FROM employees e WHERE job LIKE '%INSTRUCTOR%';
SELECT /*+ INDEX(i.e emp_sal_idx) */ ...
FROM instructors i WHERE i.salary > 5000;
9. 힌트 무시 조건
- 키워드 뒤가 아닐 때
 - 문법 오류
 - 충돌 (RULE vs CBO, INDEX_FFS 조건 불일치 등)
 - 조건 불충족 (USE_HASH인데 키 없음, PARALLEL 불가 상황)
 - 무시 시 → 단순 주석 처리
 
10. 검증 방법
- DBMS_XPLAN.DISPLAY_CURSOR('ALLSTATS LAST'): A-Rows vs E-Rows 확인
 - SQL Monitoring (MONITOR 힌트): 실시간 실행 단계 추적
 - SQL Trace + tkprof: Parse/Exec/Fetch 시간 확인
 - Plan Hash Value: 동일 계획 여부 확인
 
🚨 힌트 사용 시 주의사항
- 통계 변경 시 무력화: 통계 재수집 후 오히려 성능 저하
 - 데이터 분포 변화 취약: NL 유리 → 대량 증가 시 Hash가 더 나을 수 있음
 - 이식성/유지보수성 저하: DB 버전 업 시 성능 악화 위험
 - 절대적 강제 아님: 조건 불일치 시 무시됨
 
결론
- 힌트는 옵티마이저 보정 도구
 - 목적: 비효율적인 실행계획 보정
 - 효과: 응답속도 개선, 리소스 절감, 대량 처리 안정화
 - 원칙: SQL 리팩토링/통계/인덱스로 먼저 → 힌트는 최후의 수단
 - 검증 필수: DBMS_XPLAN, SQL Monitoring, tkprof
 
728x90
    
    
  반응형
    
    
    
  '데이터베이스 > Oracle' 카테고리의 다른 글
| [DB/Oracle] SQL 튜닝 내용 정리 : 개념부터 DBMS_XPLAN, MONITOR까지 (0) | 2025.09.30 | 
|---|---|
| [DB/Oracle] 오라클 SQL 옵티마이저 (0) | 2025.09.25 | 
| [DB/Oracle] Oracle SQL Join : 논리적 조인 & 물리적 조인 (0) | 2025.09.25 | 
| [DB/Oracle] Oracle Index (0) | 2025.09.23 | 
| [DB/Oracle] Oracle Data Block이란? (0) | 2025.09.18 | 
										
									
										
									
										
									
										
									
댓글