SHIN STORYSHIN STORY
홈포스트C#TypeScriptNext.jsNode.js시리즈
</>SHIN STORY

sdf

탐색

  • 홈
  • 모든 포스트
  • 시리즈
  • 검색

카테고리

  • C#
  • TypeScript
  • Next.js
  • Node.js
  • Oracle

© 2025 Shin Blog. All rights reserved.

GitHubRSS
목록으로
Oracle#Oracle

오라클 힌트(hint)

SHIN

2026년 5월 12일6분 읽기19

Oracle Hint란?

Oracle Hint는 SQL 안에 넣는 특수 주석으로, 옵티마이저가 실행계획을 만들 때 특정 방향을 고려하도록 지시하는 문법입니다. 예를 들어 “이 인덱스를 써라”, “풀 테이블 스캔을 해라”, “이 테이블을 먼저 조인해라”, “Nested Loop 조인을 써라” 같은 지시를 줄 수 있습니다. Oracle 공식 문서는 힌트를 “SQL 문 안의 주석 형태 지시문”으로 설명하며, 옵티마이저 모드, 쿼리 변환, 접근 경로, 조인 순서, 조인 방식 등에 영향을 줄 수 있다고 설명합니다

힌트 사용방법

CODE
SELECT /*+ 힌트명(대상) */ 컬럼
FROM 테이블
WHERE 조건;
CODE
SELECT /*+ INDEX(e EMP_EMP_ID_PK) */
       e.employee_id, e.last_name
FROM   employees e
WHERE  e.employee_id = 100;

  힌트 주석은 SQL 블록의 첫 키워드 바로 뒤에 와야 하고, /+ ... / 형태에서 +는 / 바로 뒤에 붙어야 합니다. 여러 힌트는 하나의 힌트 주석 안에 공백으로 구분해 넣습니다. 예를 들어 SELECT /+ FULL(e) PARALLEL(e 4) */ ... 형태입니다.

주의할 점은 테이블에 별칭을 썼다면 힌트에도 테이블명이 아니라 별칭을 써야 한다는 것입니다. 예를 들어 FROM employees e라면 FULL(employees)가 아니라 FULL(e)를 써야 합니다. Oracle 공식 문서도 별칭이 있으면 힌트에서 별칭을 사용해야 한다고 설명합니다.

특정 인덱스 사용 유도

CODE
-- employees 테이블의 별칭 e에 대해 EMP_DEPARTMENT_IX 인덱스를 사용하도록 유도
SELECT /*+ INDEX(e EMP_DEPARTMENT_IX) */
       e.employee_id, e.department_id, e.last_name
FROM   employees e
WHERE  e.department_id = 50;

 

CODE
-- department_id 조건의 선택도가 높고, 인덱스 접근이 유리하다고 판단될 때
SELECT /*+ INDEX(e EMP_DEPARTMENT_IX) */
       *
FROM   employees e
WHERE  e.department_id = 10;

 

FULL 힌트 — Full Table Scan 유도

CODE
--employees 테이블을 인덱스가 아니라 Full Table Scan으로 읽도록 유도
--조건에 맞는 데이터가 너무 많아서 인덱스보다 전체 스캔이 더 빠를 때
SELECT /*+ FULL(e) */
       e.employee_id, e.last_name
FROM   employees e
WHERE  e.last_name LIKE 'Kim%';
CODE
SELECT /*+ FULL(o) */
       COUNT(*)
FROM   orders o
WHERE  o.order_date >= DATE '2025-01-01';

 

NO_INDEX 힌트 — 특정 인덱스 사용 방지

CODE
--EMP_DEPARTMENT_IX 인덱스를 사용하지 말라고 유도
--특정 인덱스를 타면 오히려 느려지는 경우, 또는 잘못된 실행계획을 피하고 싶을 때 사용합니다.
SELECT /*+ NO_INDEX(e EMP_DEPARTMENT_IX) */
       e.employee_id, e.department_id
FROM   employees e
WHERE  e.department_id = 50;

 

LEADING 힌트 — 조인 순서 지정

CODE
--departments d를 먼저 읽고, 그 다음 employees e를 조인하도록 유도
--LEADING은 여러 테이블 중 어떤 테이블부터 조인할지 지정할 때 자주 사용합니다
SELECT /*+ LEADING(d e) */
       e.employee_id, e.last_name, d.department_name
FROM   departments d
JOIN   employees e
ON     e.department_id = d.department_id
WHERE  d.department_id = 10;

 

ORDERED 힌트 — FROM 절 순서대로 조인

CODE
--FROM 절에 적힌 순서대로 departments → employees 순서로 조인
--LEADING은 힌트 안에서 조인 순서를 명시하는 방식이고, ORDERED는 FROM 절에 적힌 순서를 따르게 하는 방식입니다.
SELECT /*+ ORDERED */
       e.employee_id, d.department_name
FROM   departments d,
       employees e
WHERE  e.department_id = d.department_id
AND    d.department_id = 10;

 

USE_NL 힌트 — Nested Loop Join 유도

CODE
--d를 먼저 읽고, e를 Nested Loop 방식으로 조인
--실무적으로는 소량의 기준 데이터로 다른 테이블을 인덱스 조회할 때 유리합니다.
SELECT /*+ LEADING(d e) USE_NL(e) */
       e.employee_id, e.last_name, d.department_name
FROM   departments d
JOIN   employees e
ON     e.department_id = d.department_id
WHERE  d.department_id = 10;
CODE
SELECT /*+ LEADING(c o) USE_NL(o) INDEX(o ORDERS_CUST_ID_IX) */
       c.customer_name, o.order_id, o.order_date
FROM   customers c
JOIN   orders o
ON     o.customer_id = c.customer_id
WHERE  c.customer_id = 100;

 

USE_HASH 힌트 — Hash Join 유도

CODE
--employees와 departments를 Hash Join 방식으로 조인하도록 유도
--대량 데이터 조인에서는 Nested Loop보다 Hash Join이 유리한 경우가 많습니다.
SELECT /*+ USE_HASH(e d) */
       e.employee_id, d.department_name
FROM   employees e
JOIN   departments d
ON     e.department_id = d.department_id;
CODE
SELECT /*+ USE_HASH(o i) */
       o.order_id, i.item_id
FROM   orders o
JOIN   order_items i
ON     i.order_id = o.order_id
WHERE  o.order_date >= DATE '2025-01-01';

USE_MERGE 힌트 — Sort Merge Join 유도

CODE
--Sort Merge Join을 사용하도록 유도
--양쪽 데이터가 이미 정렬되어 있거나, 정렬 후 병합이 유리한 경우 사용할 수 있습니다.
SELECT /*+ USE_MERGE(e d) */
       e.employee_id, d.department_name
FROM   employees e
JOIN   departments d
ON     e.department_id = d.department_id;

 

ALL_ROWS 힌트 — 전체 처리량 최적화

CODE
--전체 결과를 처리하는 총 비용을 최소화하는 방향으로 최적화
--배치, 리포트, 대량 조회성 SQL에 어울립니다.
SELECT /*+ ALL_ROWS */
       *
FROM   sales
WHERE  sales_date >= DATE '2025-01-01';

 

FIRST_ROWS(n) 힌트 — 앞쪽 n건 빠른 반환 유도

CODE
--전체 결과보다 처음 10건을 빠르게 가져오는 방향으로 최적화
--페이징 조회, 검색 화면, 목록 화면에서 자주 사용합니다.
SELECT /*+ FIRST_ROWS(10) */
       employee_id, last_name, salary
FROM   employees
WHERE  department_id = 50
ORDER BY salary DESC;
CODE
SELECT *
FROM (
    SELECT /*+ FIRST_ROWS(20) */
           p.product_id, p.product_name, p.price
    FROM   products p
    WHERE  p.category_id = 10
    ORDER BY p.price DESC
)
WHERE ROWNUM <= 20;

 

PARALLEL 힌트 — 병렬 처리 유도

CODE
--orders 테이블을 병렬도 4로 읽도록 유도
--대용량 테이블 집계, 배치성 쿼리에서 사용할 수 있습니다.
SELECT /*+ PARALLEL(o 4) */
       COUNT(*)
FROM   orders o
WHERE  o.order_date >= DATE '2025-01-01';
CODE
INSERT /*+ APPEND PARALLEL(t 4) */
INTO   target_table t
SELECT /*+ PARALLEL(s 4) */
       *
FROM   source_table s;

 

APPEND 힌트 — Direct Path Insert 유도

CODE
--일반 Insert 경로가 아니라 Direct Path Insert를 유도
--대량 적재 시 자주 사용합니다. 다만 Undo, Redo, Lock, 병렬 DML, 인덱스 유지 비용 등을 함께 고려해야 합니다.
INSERT /*+ APPEND */
INTO   sales_backup
SELECT *
FROM   sales
WHERE  sales_date < DATE '2024-01-01';

 

힌트가 무시되는 경우

힌트는 “반드시 무조건 적용된다”기보다는, 옵티마이저가 가능한 조건에서 실행계획 선택에 반영하는 지시입니다. Oracle 문서는 문법 오류, 잘못된 객체명, 충돌하는 힌트, 쿼리 변환으로 인해 대상이 사라지는 경우 등에서는 힌트가 무시될 수 있고, 무시되어도 일반적으로 에러를 내지 않는다고 설명합니다.

예를 들어 아래는 서로 충돌할 수 있습니다.

CODE
SELECT /*+ FULL(e) INDEX(e EMP_DEPARTMENT_IX) */
       *
FROM   employees e
WHERE  e.department_id = 10;

FULL(e)는 Full Table Scan을 유도하고, INDEX(e ...)는 인덱스 스캔을 유도하므로 서로 방향이 다릅니다. 이런 식의 충돌 힌트는 의도와 다르게 무시될 수 있습니다.  

힌트 적용 여부 확인 방법

CODE
EXPLAIN PLAN FOR
SELECT /*+ INDEX(e EMP_EMP_ID_PK) */
       COUNT(*)
FROM   employees e
WHERE  e.employee_id = 100;

SELECT *
FROM   TABLE(DBMS_XPLAN.DISPLAY(format => 'ALL'));

Oracle 19c 이후에는 힌트 사용 리포트를 통해 어떤 힌트가 사용됐고 어떤 힌트가 무시됐는지 확인하기 쉬워졌습니다. DBMS_XPLAN.DISPLAY, DISPLAY_CURSOR 등에서 HINT_REPORT 포맷을 지정하면 힌트 리포트를 볼 수 있고, ALL은 사용/미사용 힌트를 모두 보여주며 TYPICAL은 최종 계획에서 사용되지 않은 힌트를 중심으로 보여줍니다.

 

CODE
--실행된 SQL 기준으로 확인
SELECT *
FROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST +HINT_REPORT'));

 

실무 예제

예제1 : 소량 기준 테이블 → 대량 테이블 인덱스 Nested Loop

CODE
SELECT /*+ LEADING(c o) USE_NL(o) INDEX(o ORDERS_CUST_ID_IX) */
       c.customer_id,
       c.customer_name,
       o.order_id,
       o.order_date
FROM   customers c
JOIN   orders o
ON     o.customer_id = c.customer_id
WHERE  c.customer_id = 100;
  1. customers c를 먼저 읽음
  2. orders o는 Nested Loop로 조인
  3. orders는 ORDERS_CUST_ID_IX 인덱스를 사용하도록 유도

이런 SQL은 customers에서 1명 또는 소수 고객만 조회하고, 해당 고객의 주문을 인덱스로 찾는 구조일 때 적합합니다.

예제 2: 대량 테이블끼리 Hash Join

CODE
SELECT /*+ USE_HASH(o i) FULL(o) FULL(i) */
       o.order_id,
       i.product_id,
       i.quantity
FROM   orders o
JOIN   order_items i
ON     i.order_id = o.order_id
WHERE  o.order_date >= DATE '2025-01-01';
  1. orders, order_items를 Full Scan으로 읽도록 유도
  2. 두 테이블을 Hash Join으로 조인

예제 3: 페이징 화면에서 첫 20건 빠르게 조회

CODE
SELECT *
FROM (
    SELECT /*+ FIRST_ROWS(20) INDEX(p PRODUCTS_CATEGORY_PRICE_IX) */
           p.product_id,
           p.product_name,
           p.price
    FROM   products p
    WHERE  p.category_id = 10
    ORDER BY p.price DESC
)
WHERE ROWNUM <= 20;
  1. 처음 20건을 빠르게 반환하는 방향으로 최적화
  2. 카테고리/가격 관련 인덱스 사용 유도

제 4: 대량 백업성 Insert

CODE
INSERT /*+ APPEND PARALLEL(sb 4) */
INTO   sales_backup sb
SELECT /*+ PARALLEL(s 4) FULL(s) */
       *
FROM   sales s
WHERE  s.sales_date < DATE '2024-01-01';
  1. sales를 병렬 Full Scan
  2. sales_backup에 Direct Path Insert 유도
  3. 병렬도 4 사용 유도  

힌트 사용 시 권장 기준

힌트는 “옵티마이저보다 내가 더 잘 안다”는 전제에서 쓰는 도구입니다. 그래서 먼저 통계정보, 인덱스 설계, SQL 조건식, 조인 조건, 파티션 프루닝 등을 점검하는 것이 좋습니다. Oracle 공식 문서도 데이터와 DB 환경 변화로 힌트가 낡거나 부정적인 영향을 줄 수 있으므로, 힌트는 테스트 용도로 유용하지만 장기적인 실행계획 관리는 SQL Tuning Advisor, SQL Plan Management, SQL Performance Analyzer 같은 도구를 권장한다고 설명합니다.

  1. 현재 실행계획 확인
  2. 통계정보 최신 여부 확인
  3. 인덱스/조건식/조인 방식 점검
  4. 힌트 적용 후 실행계획과 실제 수행시간 비교
  5. DBMS_XPLAN.DISPLAY_CURSOR(..., '+HINT_REPORT')로 힌트 적용 여부 확인
  6. 운영 반영 전 데이터량 변화 시나리오까지 테스트

핵심만 정리하면, Oracle Hint는 성능 튜닝에 강력하지만 남용하면 SQL이 환경 변화에 취약해집니다. 특히 INDEX, FULL, LEADING, USE_NL, USE_HASH, FIRST_ROWS, PARALLEL 정도가 실무에서 가장 자주 쓰입니다.

공유
S

SHIN

.NET 개발자입니다

GitHub
클릭원스 업데이트 체크  (비동기)

이전 포스트

클릭원스 업데이트 체크 (비동기)

댓글