서브쿼리 성능 최적화 – 서브쿼리에서 성능을 끌어올리는 5가지 방법

Posted by heoncode
2025. 4. 16. 09:12 오라클 실무 쿼리 튜닝
반응형
SMALL

서브쿼리는 SQL에서 매우 유용하게 쓰이는 기능이지만, 잘못 사용하면 전체 쿼리 성능을 크게 떨어뜨릴 수 있습니다. 특히 실무 환경에서는 서브쿼리가 느려지는 원인을 정확히 파악하고, 효율적으로 개선하는 것이 중요합니다. 이 글에서는 서브쿼리를 성능 저하 없이 사용하는 실전 최적화 팁 5가지를 소개합니다.

1. 불필요한 서브쿼리는 조인으로 대체

단순히 조건 확인용으로 쓰이는 서브쿼리는 조인으로 바꾸는 것이 더 나은 경우가 많습니다. 서브쿼리가 테이블을 여러 번 조회할 가능성이 있기 때문에, 조인으로 바꾸면 물리적 접근 횟수를 줄일 수 있습니다.

예시:

SELECT E.ename, D.dname
FROM emp E
JOIN dept D ON E.deptno = D.deptno
WHERE D.loc = 'DALLAS';

이와 같은 방식으로 WHERE 절 서브쿼리를 JOIN으로 바꾸면 쿼리 최적화에 유리해집니다.

2. 스칼라 서브쿼리는 주의해서 사용

SELECT 절에서 서브쿼리를 쓰는 경우, 특히 테이블 로우마다 반복 실행되기 때문에 성능 저하의 주범이 될 수 있습니다. 이런 경우도 조인으로 바꾸는 게 훨씬 효율적입니다.

비효율적인 예:

SELECT ename,
       (SELECT dname FROM dept D WHERE D.deptno = E.deptno) AS dname
FROM emp E;

대체 가능한 방식:

SELECT E.ename, D.dname
FROM emp E
JOIN dept D ON E.deptno = D.deptno;

3. EXISTS vs IN – 상황에 따라 구분

서브쿼리 안에서 IN을 사용하는 경우와 EXISTS를 사용하는 경우는 성능 차이가 발생할 수 있습니다. 일반적으로 EXISTS는 조건을 만족하는 첫 행만 찾으면 종료되므로 더 빠르게 처리됩니다.

-- EXISTS 예시
SELECT ename
FROM emp E
WHERE EXISTS (SELECT 1 FROM dept D WHERE D.deptno = E.deptno AND D.loc = 'DALLAS');

-- IN 예시
SELECT ename
FROM emp
WHERE deptno IN (SELECT deptno FROM dept WHERE loc = 'DALLAS');

두 방식 모두 테스트 후 더 빠른 쪽을 선택하는 것이 좋습니다.

4. 서브쿼리 결과를 뷰 또는 인라인 뷰로 활용

복잡한 서브쿼리를 여러 번 사용할 경우, 해당 서브쿼리를 뷰로 분리하거나 인라인 뷰로 활용하는 것이 성능을 개선하는 데 도움이 됩니다.

SELECT E.ename, V.dname
FROM emp E
JOIN (SELECT deptno, dname FROM dept WHERE loc = 'DALLAS') V
  ON E.deptno = V.deptno;

이렇게 하면 서브쿼리 결과를 캐시처럼 활용할 수 있어 반복 실행을 방지할 수 있습니다.

5. 서브쿼리 내부에도 인덱스가 필요하다

서브쿼리가 아무리 작아도, 내부에서 WHERE, JOIN, IN 등을 사용하는 경우 해당 조건 컬럼에 인덱스가 없다면 성능 병목이 생깁니다. 서브쿼리라고 해서 옵티마이저가 무시하지 않기 때문에, 내부 테이블의 통계 정보 및 인덱스 구성도 반드시 확인해야 합니다.


서브쿼리는 무조건 나쁘다는 오해가 많지만, 목적과 상황에 맞게 작성하면 충분히 강력한 도구가 됩니다. 위의 팁들을 실제 쿼리에 적용해보고, 옵티마이저 실행 계획을 분석해 보며 성능 개선 효과를 확인해보시기 바랍니다.


#서브쿼리 #SQL성능최적화 #오라클튜닝 #서브쿼리조인 #EXISTSvsIN #인라인뷰

반응형
LIST

WHERE 조건은 인덱스를 타는데 왜 느릴까? – INDEX RANGE SCAN의 함정과 대처법

Posted by heoncode
2025. 4. 15. 14:12 오라클 실무 쿼리 튜닝
반응형
SMALL

오라클에서 실행 계획을 보면 INDEX RANGE SCAN이 잡히는 경우가 있습니다. 일반적으로 이는 인덱스를 잘 활용하고 있다는 의미로 받아들여지지만, 실제로는 쿼리 성능이 기대만큼 나오지 않거나 오히려 느려지는 경우도 존재합니다.

이번 글에서는 인덱스를 타고 있음에도 성능이 저하되는 이유를 다양한 관점에서 분석하고, 이를 해결하기 위한 실무적인 팁을 함께 소개합니다.

1. INDEX RANGE SCAN이란?

INDEX RANGE SCAN은 인덱스를 통해 일부 범위만 탐색하는 방식입니다. 예를 들어 조건절이 WHERE AGE BETWEEN 20 AND 30처럼 범위 조건일 때 사용됩니다.

실행 계획에서 다음처럼 보입니다:

INDEX RANGE SCAN on IDX_EMP_AGE

인덱스가 사용되므로 성능이 좋을 것으로 기대되지만, 아래와 같은 이유로 성능이 떨어질 수 있습니다.

2. 인덱스를 타는데 느린 이유

① TABLE ACCESS BY ROWID가 병목

인덱스를 타더라도 최종 데이터를 읽기 위해 테이블 본문을 조회해야 하는 경우가 많습니다. 이는 TABLE ACCESS BY ROWID 단계에서 발생하며, 인덱스로 찾은 ROWID를 따라가 테이블을 반복해서 읽는 구조입니다.

행 수가 많다면 디스크 I/O가 상당히 증가합니다.

② 인덱스 조건은 탔지만 FILTER 조건에서 제외됨

예를 들어 다음 쿼리에서:

SELECT * FROM EMP
WHERE AGE BETWEEN 30 AND 40 AND GENDER = 'M';

AGE 컬럼에만 인덱스가 있을 경우, GENDER = 'M' 조건은 인덱스에서 필터링되지 않고, 테이블 조회 후 FILTER 단계에서 제거됩니다. 불필요한 행을 먼저 읽는 구조가 되므로 비효율적입니다.

③ 인덱스 컬럼이 NULLABLE이거나 정렬도가 낮음

조건 컬럼이 NULL 값을 많이 포함하거나, 정렬도가 낮은 컬럼일 경우 오라클 옵티마이저는 예측보다 많은 범위를 탐색하게 됩니다. 결국 인덱스를 사용하고도 실제 I/O 비용은 높은 상황이 됩니다.

④ 조건 범위가 지나치게 넓음

INDEX RANGE SCAN이라도 WHERE AGE > 0 같은 조건이라면 거의 전체 테이블을 읽는 것과 다름없는 범위를 탐색하게 됩니다. 이럴 경우에는 오히려 FTS(FULL TABLE SCAN)가 더 빠를 수 있습니다.

3. 개선 방법

✅ 필요한 컬럼만 SELECT

필요한 컬럼만 조회하면 INDEX ONLY SCAN으로 전환할 수 있습니다. 이 경우 테이블을 조회하지 않고 인덱스만으로 결과를 반환하므로 매우 빠릅니다.

예시:

SELECT AGE FROM EMP WHERE AGE BETWEEN 20 AND 30;

AGE만 필요하면 INDEX ONLY SCAN이 가능해집니다.

✅ 복합 인덱스 설계

위 예시처럼 AGEGENDER를 모두 조건으로 사용하는 경우, 두 컬럼을 포함하는 복합 인덱스를 설계하면 FILTER 단계를 제거할 수 있습니다.

CREATE INDEX IDX_EMP_AGE_GENDER ON EMP(AGE, GENDER);

✅ 인덱스 힌트와 실행 계획 확인

실행 계획을 항상 확인하여 실제 인덱스가 어떤 방식으로 사용되는지 점검해야 합니다. 때로는 옵티마이저가 인덱스를 사용하더라도 성능상 불리할 수 있으며, 이럴 경우 힌트를 통해 명시적으로 FTS로 유도하는 것도 방법입니다.


마무리하며

실행 계획에 INDEX RANGE SCAN이 떴다고 해서 반드시 빠른 쿼리라고 단정지을 수는 없습니다. 실제 쿼리 성능은 테이블 접근 방식, 조건의 범위, 필터 조건 처리 방식 등 다양한 요소가 복합적으로 작용합니다.

이전에 작성한 INDEX가 안 잡힐 때 확인할 조건SELECT COUNT(*)가 느릴 때 – 성능 개선 전략과 대안 글과 함께 참고하면, 인덱스 관련 성능 이슈를 보다 정확하게 진단하고 해결할 수 있습니다.

#오라클 #SQL튜닝 #인덱스성능 #INDEXRANGESCAN #실행계획 #실무팁 #SQL최적화

반응형
LIST

SELECT COUNT(*)가 느릴 때 – 성능 개선 전략과 대안

Posted by heoncode
2025. 4. 15. 12:00 오라클 실무 쿼리 튜닝
반응형
SMALL

오라클에서 SELECT COUNT(*)는 가장 단순한 집계 함수 중 하나입니다. 하지만 실무에서는 이 간단한 쿼리조차 예상보다 오래 걸리는 경우가 많습니다. 특히 대용량 테이블을 대상으로 할 때 COUNT가 심각하게 느려진다면, 단순히 "집계 함수니까 느리다"는 설명만으로는 부족합니다.

이번 글에서는 SELECT COUNT(*)가 느려지는 원인을 분석하고, 이를 개선할 수 있는 실질적인 전략들을 정리합니다.

1. COUNT(*)는 인덱스를 사용할까?

많은 개발자들이 SELECT COUNT(*)가 자동으로 인덱스를 사용할 것이라 생각하지만, 조건절이 없다면 테이블 전체를 스캔하게 됩니다. 오라클은 COUNT를 계산하기 위해 반드시 모든 행을 확인해야 하며, 조건이 없다면 풀 테이블 스캔(Full Table Scan)이 수행됩니다.

조건이 있는 경우, 해당 조건에 적절한 인덱스가 존재한다면 옵티마이저가 인덱스를 사용할 수도 있습니다.

예시:

SELECT COUNT(*) FROM EMP WHERE DEPTNO = 10;

위 쿼리는 DEPTNO 컬럼에 인덱스가 존재한다면 INDEX RANGE SCAN으로 수행될 수 있습니다.

2. COUNT가 느린 주요 원인

다음은 실무에서 자주 발생하는 COUNT 성능 저하 원인입니다:

  • 조건이 없어 전체 테이블을 스캔하는 경우
  • 대용량 테이블에 파티션이 없거나 적절하지 않은 경우
  • 블록 내 저장된 행 수가 불균형하거나, CHAINED ROW가 많은 경우
  • VIEW나 서브쿼리 내부의 조인 비용이 높을 경우
  • DISTINCT, GROUP BY 등이 함께 쓰이는 경우

단순 COUNT일지라도 내부적으로 의외로 복잡한 연산 경로를 탈 수 있습니다. 실행 계획(EXPLAIN PLAN) 확인이 필요합니다.

3. 성능 개선 전략

✅ 조건을 최대한 활용하라

WHERE 절이 없는 COUNT는 테이블 전체를 스캔하기 때문에 매우 느립니다. 가능한 조건을 활용해 인덱스를 탈 수 있도록 쿼리를 구성하는 것이 좋습니다.

예시:

SELECT COUNT(*) FROM LOG_TABLE WHERE CREATE_DT >= SYSDATE - 1;

조건이 있다면 해당 컬럼에 인덱스를 추가하여 효율적인 COUNT가 가능합니다.

✅ 파티션 테이블 도입

대용량 로그성 테이블에서는 파티셔닝(partitioning)이 COUNT 성능에 큰 영향을 미칩니다. 예를 들어 일자별 RANGE 파티션을 구성하면, 최근 1일치 데이터에 대한 COUNT를 빠르게 조회할 수 있습니다.

✅ COUNT 대신 메타데이터 활용

전체 행 수만 필요하다면 DBA_TABLES, ALL_TABLES, USER_TABLESNUM_ROWS 컬럼을 사용할 수 있습니다. 단, 통계 정보가 최신이어야 정확합니다.

예시:

SELECT NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'EMP';

이 값은 DBMS_STATS.GATHER_TABLE_STATS 수행 시 갱신됩니다.

✅ COUNT 결과 캐싱 고려

변경이 자주 없는 테이블이나 집계용 로그 테이블의 경우, COUNT 결과를 별도 컬럼이나 캐시 테이블에 저장해두고 주기적으로 갱신하는 전략도 있습니다. 특히 대시보드 등에서 반복적으로 COUNT 쿼리가 실행된다면 효과적입니다.


마무리하며

SELECT COUNT(*)는 단순하지만, 그만큼 무심코 쓰기 쉬운 쿼리입니다. 특히 대용량 테이블에서는 성능 이슈로 이어지기 쉬우므로, COUNT 쿼리를 쓸 때도 항상 실행 계획을 점검하고 조건, 인덱스, 파티션 여부 등을 함께 고려해야 합니다.

이전에 작성한 INDEX가 안 잡힐 때 확인할 조건 글과 함께 참고하면, 쿼리의 성능 문제를 보다 체계적으로 점검할 수 있습니다.

#오라클 #SQL튜닝 #카운트성능 #대용량테이블 #파티셔닝 #DBMS_STATS #실무팁

반응형
LIST

INDEX가 안 잡힐 때 확인할 조건 – 오라클 인덱스 미사용 원인 정리

Posted by heoncode
2025. 4. 15. 09:37 오라클 실무 쿼리 튜닝
반응형
SMALL

오라클에서 인덱스를 생성했음에도 불구하고 실제 실행 계획에서 인덱스가 사용되지 않는 경우는 실무에서 자주 발생하는 문제입니다. 이 글에서는 인덱스가 정상적으로 사용되지 않을 때 확인해야 할 주요 원인들을 정리하고, 각각의 사례에 대한 설명과 개선 방법을 함께 다룹니다.

1. WHERE 절의 컬럼에 함수 사용

인덱스가 설정된 컬럼에 함수가 사용되면 옵티마이저는 해당 인덱스를 사용할 수 없습니다.

예시:

SELECT * FROM EMP WHERE TO_CHAR(HIREDATE, 'YYYY') = '2020';

위 쿼리는 HIREDATE 컬럼에 함수가 적용되어 있어 인덱스를 타지 않습니다. 가능한 방법은 함수를 제거하거나, 함수 기반 인덱스(function-based index) 를 따로 생성하는 것입니다.

2. 형 변환 발생

숫자 타입 컬럼에 문자열을 비교하거나, 반대로 문자열 컬럼에 숫자를 비교하는 경우 암묵적 형 변환이 일어나 인덱스가 무시될 수 있습니다.

예시:

SELECT * FROM EMP WHERE EMPNO = '7369';  -- EMPNO가 NUMBER형일 경우

이 경우 '7369'가 암묵적으로 TO_NUMBER 처리되며 인덱스가 무시됩니다. 조건절의 리터럴 타입을 컬럼 타입과 맞추는 것이 중요합니다.

3. NOT 조건, 부정 연산자

!=, NOT IN, NOT EXISTS 등 부정 조건이 들어가는 경우 인덱스 사용률이 현저히 낮아집니다. 특히 범위 탐색이 불가능한 조건들은 옵티마이저가 풀 스캔을 선택하는 경향이 있습니다.

가능한 경우 NOT IN 대신 OUTER JOIN + IS NULL 방식 등으로 구조를 바꾸는 것이 도움이 됩니다.

4. 컬럼에 NULL 조건

WHERE COLUMN = NULL 또는 IS NOT NULL 조건도 인덱스 사용을 방해합니다. 오라클 인덱스는 NULL 값을 기본적으로 저장하지 않기 때문에, IS NULL 조건도 인덱스를 타지 않을 가능성이 높습니다.

NULL이 포함되는 조건을 자주 조회한다면 비트맵 인덱스를 고려할 수 있습니다.

5. OR 조건

WHERE A = 'X' OR B = 'Y' 처럼 OR 조건이 있는 경우 옵티마이저는 전체 조건에 대해 풀스캔을 선택할 수 있습니다. 이 경우는 쿼리를 UNION ALL로 분리하는 방식으로 인덱스 사용을 유도할 수 있습니다.

예시:

SELECT * FROM EMP WHERE A = 'X'
UNION ALL
SELECT * FROM EMP WHERE B = 'Y';

6. 통계 정보 미갱신

DBMS_STATS를 통해 테이블 및 인덱스의 통계 정보가 최신 상태로 유지되지 않으면 옵티마이저가 잘못된 판단을 할 수 있습니다.

정기적으로 통계를 갱신하는 스케줄을 운영하는 것이 중요합니다.

7. 인덱스 컬럼 순서 불일치

다중 컬럼 인덱스의 경우, WHERE 절 조건이 인덱스의 선두 컬럼부터 일치해야만 해당 인덱스가 유효하게 작동합니다.

예시: 인덱스 (COL1, COL2)일 때

SELECT * FROM TAB WHERE COL2 = 'Y'; -- 인덱스 사용 불가

COL1 조건이 빠지면 옵티마이저는 인덱스를 사용할 수 없습니다. 컬럼 순서를 기준으로 조건절을 구성해야 합니다.


위에서 다룬 원인들은 대부분 실무에서 흔하게 접할 수 있는 사례입니다. 오라클에서 인덱스가 사용되지 않을 경우, 실행 계획만 보는 것에 그치지 않고 이러한 조건들을 하나씩 점검해보는 습관이 필요합니다. 특히 인덱스를 기준으로 튜닝이 필요한 경우, 조건절을 어떻게 작성하느냐가 성능에 직접적인 영향을 주는 핵심 요소입니다.

이전에 작성한 HAVING 절과 WHERE 절의 차이 글처럼, 조건을 어떻게 거느냐는 쿼리 성능에 큰 차이를 만들 수 있습니다. 인덱스 미사용 문제가 발생했다면 위 내용을 기준으로 체크리스트처럼 점검해보는 것을 추천합니다.

#오라클 #SQL튜닝 #인덱스미사용 #오라클쿼리 #형변환 #통계정보 #함수사용주의 #실무팁

반응형
LIST

인라인 뷰 vs 테이블 조인 – 같은 결과, 다른 처리 방식

Posted by heoncode
2025. 4. 14. 09:11 오라클 실무 쿼리 튜닝
반응형
SMALL

SQL을 작성하다 보면 인라인 뷰테이블 조인 중 어떤 방식이 더 좋은지 고민하게 되는 경우가 많습니다. 겉으로는 같은 결과를 도출할 수 있어 보이지만, 실제로는 처리 방식이나 성능 측면에서 차이를 보일 수 있습니다. 이번 글에서는 인라인 뷰와 테이블 조인의 차이점과 실무에서 선택 기준을 어떻게 잡아야 하는지 살펴보겠습니다.

인라인 뷰란?

인라인 뷰는 FROM 절 안에 작성된 서브쿼리로, 마치 임시 테이블처럼 작동합니다. 복잡한 데이터를 미리 가공한 후, 이를 다시 외부 쿼리에서 조인하거나 필터링할 수 있게 해줍니다.

예시:

SELECT emp.empno, emp.ename, dept.dept_name
FROM (
    SELECT * FROM emp WHERE sal > 3000
) emp
JOIN dept ON emp.deptno = dept.deptno;

위 쿼리는 먼저 급여가 3000을 초과하는 직원만 추출한 다음, 해당 결과를 기반으로 부서 테이블과 조인합니다. emp는 인라인 뷰로 사용되고 있습니다.

테이블 조인 방식

테이블 조인은 일반적으로 여러 테이블을 직접 JOIN하여 필요한 데이터를 한 번에 조회하는 방식입니다. 조건이 단순하거나 중간 가공이 필요 없는 경우에 사용됩니다.

예시:

SELECT e.empno, e.ename, d.dept_name
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.sal > 3000;

이 방식은 별도의 가공 없이 바로 조건을 걸고, 조인을 수행합니다. 데이터 양이 많지 않거나 단순한 경우, 옵티마이저가 효율적으로 처리해줄 가능성이 높습니다.

어떤 차이가 있을까?

두 방식은 결과적으로 동일한 데이터를 반환할 수 있지만, 옵티마이저의 처리 방식에 따라 성능에 차이를 보일 수 있습니다. 다음은 주요 차이점입니다:

  • 옵티마이저 최적화 수준: 인라인 뷰는 독립적으로 처리되는 경우가 많아 옵티마이저가 전체 쿼리의 실행 계획을 유연하게 조정하지 못하는 경우가 있습니다.
  • 필터 순서 제어: 인라인 뷰 내에서는 WHERE 절이 먼저 처리되므로, 복잡한 필터링이나 가공을 먼저 수행하고 조인을 하게 됩니다.
  • 읽어야 할 데이터 양: 조건에 따라 먼저 필터링을 해서 줄일 수 있다면 인라인 뷰가 더 효율적일 수 있지만, 그렇지 않으면 오히려 중복 연산이 될 수도 있습니다.

실무에서의 선택 기준

  1. 필터링 우선 여부: 먼저 데이터를 가공해야만 하는 경우 → 인라인 뷰
  2. 단순 조건 조합일 경우: 성능을 위해 옵티마이저에 맡기고 싶은 경우 → 테이블 조인
  3. 읽어야 할 데이터 양이 많은 경우: 인라인 뷰가 오히려 느려질 수 있음 → 테이블 조인 권장
  4. 복잡한 가공이나 집계를 별도로 해야 하는 경우: 인라인 뷰 활용

결론

인라인 뷰와 테이블 조인은 단순히 문법 차이가 아닌, 옵티마이저의 쿼리 처리 방식에 영향을 줍니다. 따라서 동일한 결과를 반환한다고 해서 성능도 같을 거라는 가정은 위험합니다. 실무에서는 실제 실행 계획을 확인하면서, 상황에 맞는 방식을 선택하는 것이 중요합니다.


#SQL튜닝 #인라인뷰 #테이블조인 #SQL성능 #서브쿼리 #오라클쿼리팁 #오라클튜닝

반응형
LIST

INDEX RANGE SCAN과 FULL SCAN, 언제 어떤 게 빠를까?

Posted by heoncode
2025. 4. 9. 09:14 오라클 실무 쿼리 튜닝
반응형
SMALL

INDEX RANGE SCAN과 FULL SCAN, 언제 어떤 게 빠를까?

오라클의 실행계획에서 자주 등장하는 INDEX RANGE SCAN과 FULL TABLE SCAN은 단순히 “인덱스가 빠르다”는 기준으로 선택할 수 없습니다. 데이터 양, 인덱스 구조, 조건절 등에 따라 성능 차이는 극적으로 달라질 수 있으며, 실무에서는 오히려 FULL SCAN이 더 나은 선택일 때도 있습니다. 이번 글에서는 두 방식의 차이와 선택 기준을 살펴보겠습니다.

1. INDEX RANGE SCAN – 인덱스를 이용한 범위 검색

INDEX RANGE SCAN은 주어진 조건절에 따라 인덱스에서 일부 구간만을 읽어오는 방식입니다. 흔히 사용하는 비교 연산자(=, >, <, BETWEEN, LIKE 'abc%')가 있을 때 주로 사용되며, 조건이 인덱스 컬럼에 명확하게 매핑되어 있어야 효과적입니다.

SELECT * FROM employees WHERE department_id BETWEEN 10 AND 20;

이 경우 인덱스가 department_id에 존재하면 해당 범위만 스캔하게 되어 I/O 비용이 적고 빠른 성능을 기대할 수 있습니다.

2. FULL TABLE SCAN – 조건 무시, 테이블 전체 스캔

FULL SCAN은 인덱스를 무시하고 테이블 전체를 차례로 읽는 방식입니다. 일반적으로는 느릴 것이라 생각하지만, 다음과 같은 경우에는 오히려 효율적일 수 있습니다:

  • 조건절이 대부분의 데이터를 포함하는 경우 (예: 전체의 80% 이상)
  • 인덱스가 없는 경우
  • 병렬 처리를 통해 전체 스캔이 더 빠른 경우
  • 통계 정보나 옵티마이저 판단에 의해 인덱스 사용이 비효율적이라고 판단될 경우

예를 들어, 다음과 같은 쿼리는 조건절이 너무 광범위해 인덱스보다 FULL SCAN이 더 나을 수 있습니다.

SELECT * FROM orders WHERE order_date < SYSDATE;

3. 옵티마이저의 판단 기준

오라클 옵티마이저는 다음 요소를 기준으로 INDEX RANGE SCAN과 FULL SCAN 중 하나를 선택합니다.

  • 통계 정보: 테이블과 인덱스에 대한 최신 통계 정보
  • 데이터 분포도: 조건절이 얼마나 많은 행을 포함하는지
  • 인덱스 선택도: 인덱스가 얼마나 고유한 값들을 가지고 있는지
  • 병렬 처리 가능 여부

실제 실행계획에서 두 방식이 교체되는 경우는 의외로 많기 때문에, 반드시 EXPLAIN PLAN 또는 AUTOTRACE 등을 통해 실행계획을 확인하는 것이 중요합니다.

✅ 마무리

INDEX RANGE SCAN과 FULL TABLE SCAN은 단순히 "인덱스가 있으니까 인덱스 쓰자"는 기준으로 판단할 수 없습니다. 실무에서는 통계 정보, 데이터 양, 조건절, 병렬 처리 여부 등 다양한 요인을 종합적으로 고려해 어떤 방식이 더 빠를지 판단해야 하며, 이를 위해 실행계획 분석은 필수입니다.

#오라클 #SQL #실행계획 #성능 #인덱스 #실무팁 #쿼리튜닝

반응형
LIST

인덱스가 성능을 좌우한다? 속도 차이가 극명하게 나는 상황 3가지

Posted by heoncode
2025. 4. 8. 16:52 오라클 실무 쿼리 튜닝
반응형
SMALL

인덱스가 성능을 좌우한다? 속도 차이가 극명하게 나는 상황 3가지

SQL 성능 튜닝에서 인덱스(Index)는 빼놓을 수 없는 핵심 요소입니다. 하지만 단순히 인덱스를 만든다고 해서 항상 쿼리 속도가 빨라지는 것은 아닙니다. 인덱스가 실제로 효과를 발휘하는 조건과, 오히려 비효율을 유발하는 상황까지 모두 고려해야 실무에서 제대로 활용할 수 있습니다.

이번 글에서는 인덱스 유무에 따라 성능 차이가 극명하게 나는 3가지 상황을 실무 관점에서 정리해봅니다.


1. WHERE 절에서 다중 조건 필터링 시

SELECT * FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10 AND JOB_ID = 'IT_PROG';

만약 DEPARTMENT_ID, JOB_ID 각각 단독 인덱스만 존재하고, 두 컬럼을 동시에 포함한 복합 인덱스가 없다면 옵티마이저는 인덱스를 비효율적으로 사용할 수 있습니다. 반면 (DEPARTMENT_ID, JOB_ID)로 구성된 복합 인덱스를 만들면 효율적인 Index Range Scan이 가능해집니다.

📌 실무 팁: 자주 함께 사용하는 조건은 복합 인덱스로 묶어주는 것이 훨씬 유리합니다.


2. 정렬(ORDER BY) + 페이징 처리 시

SELECT * FROM EMPLOYEES
ORDER BY HIRE_DATE
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

HIRE_DATE 컬럼에 인덱스가 없으면 정렬 작업은 전 테이블을 스캔하면서 정렬 메모리를 소모하게 됩니다. 반면 인덱스가 있으면 인덱스 자체가 이미 정렬된 구조이므로 빠르게 필요한 범위만 추출할 수 있습니다.

📌 실무 팁: 페이징 쿼리에서는 정렬 기준 컬럼에 반드시 인덱스가 있어야 성능 이점을 누릴 수 있습니다.


3. 서브쿼리에서 EXISTS 조건 사용 시

SELECT * FROM DEPARTMENTS D
WHERE EXISTS (
  SELECT 1 FROM EMPLOYEES E
  WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
);

EMPLOYEES.DEPARTMENT_ID에 인덱스가 없다면 EXISTS 서브쿼리마다 테이블을 반복 스캔하게 되어 성능 저하가 큽니다. 반면 해당 컬럼에 인덱스가 있다면 각 부서별 매칭을 빠르게 찾을 수 있어 성능이 크게 개선됩니다.

📌 실무 팁: EXISTS 또는 IN 서브쿼리에서 조인 키에 인덱스가 없으면 심각한 성능 저하가 발생할 수 있습니다.


마무리하며

인덱스는 단순히 만들어두는 것이 아니라, 쿼리 실행 계획을 고려한 설계가 중요합니다. 특히 자주 사용되는 조합, 정렬 조건, 서브쿼리의 조인 키 등을 파악해 전략적으로 인덱스를 설계하는 것이 실무 튜닝의 핵심입니다.

항상 실행 계획(EXPLAIN PLAN)을 확인하고, 인덱스가 실제로 사용되는지 검증하는 습관을 들이세요.

#오라클 #SQL #쿼리튜닝 #인덱스 #성능개선 #실무팁 #데이터베이스

반응형
LIST

오라클 쿼리 성능을 떨어뜨리는 흔한 실수: WHERE절에서 함수 사용

Posted by heoncode
2025. 4. 8. 10:19 오라클 실무 쿼리 튜닝
반응형
SMALL

오라클에서 쿼리 성능을 높이기 위해 인덱스를 적극적으로 활용하는 것이 중요합니다.
하지만 아무 생각 없이 작성한 WHERE절 하나가 인덱스를 무용지물로 만들어버릴 수 있습니다.

대표적인 실수가 바로 컬럼에 함수를 적용하여 인덱스를 타지 못하게 만드는 것입니다.
이 문제는 단순히 성능 저하로 끝나는 것이 아니라, 대용량 테이블에서는 실제 서비스 지연으로 이어질 수 있습니다.


잘못된 예시

SELECT * FROM EMP
WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981';

위 쿼리는 HIREDATE 컬럼에 TO_CHAR 함수를 적용하고 있습니다.
이런 경우, 해당 컬럼에 인덱스가 걸려 있어도 인덱스를 사용할 수 없습니다.
결과적으로 오라클은 전체 테이블을 스캔(Full Table Scan) 하게 되고, 처리 시간이 급격히 늘어날 수 있습니다.


인덱스를 살리는 방법

함수는 리터럴 값(고정값)에 적용하고, 컬럼은 그대로 사용하는 것이 핵심입니다.

SELECT * FROM EMP
WHERE HIREDATE BETWEEN TO_DATE('1981-01-01', 'YYYY-MM-DD')
                  AND TO_DATE('1981-12-31', 'YYYY-MM-DD');

이 방식은 HIREDATE 컬럼에 함수가 적용되지 않기 때문에, 인덱스를 그대로 사용할 수 있습니다.
결과적으로 훨씬 빠른 조회 성능을 기대할 수 있죠.


실무에서 자주 발생하는 유사 사례

  • UPPER(컬럼) 또는 LOWER(컬럼) 으로 검색할 때
  • TRUNC(날짜컬럼)으로 날짜 비교할 때
  • 숫자 계산이 들어간 컬럼 비교 예: SALARY + BONUS > 5000

이런 경우 모두 인덱스를 못 타게 되어, 쿼리 성능이 나빠질 수 있습니다.
가능하면 계산이나 변환은 리터럴 값 쪽에 적용하도록 쿼리를 재작성하세요.


요약

  • WHERE절에서 컬럼에 함수 적용 시 인덱스를 사용할 수 없음
  • 성능을 위해 컬럼은 그대로 두고 리터럴에만 함수 사용
  • 실무에서는 사소한 실수 하나가 전체 시스템에 영향을 줄 수 있음
  • 습관처럼 인덱스를 고려한 WHERE절을 작성하는 것이 중요!

#오라클성능 #쿼리튜닝 #인덱스활용 #SQL성능최적화 #오라클쿼리 #개발팁

반응형
LIST

오라클 인덱스 스캔 방식 정리 - FULL SCAN, RANGE SCAN, UNIQUE SCAN 차이점

Posted by heoncode
2025. 4. 8. 01:32 오라클 실무 쿼리 튜닝
반응형
SMALL

오라클에서 쿼리의 성능을 결정짓는 중요한 요소 중 하나가 인덱스 사용 방식입니다. 이번 글에서는 실무에서 자주 마주치는 인덱스 스캔 방식 3가지를 정리해봅니다.


✅ 1. INDEX FULL SCAN

  • 전체 인덱스를 처음부터 끝까지 읽는 방식입니다.
  • 조건절의 범위가 넓거나, 정렬이 필요한 경우 사용됩니다.
  • 테이블 액세스 없이 인덱스에서만 데이터를 해결할 수 있는 경우 효율적일 수 있습니다.
  • 사용 예시: WHERE 절이 없고 ORDER BY만 있을 때 등

✅ 2. INDEX RANGE SCAN

  • 가장 일반적으로 사용되는 인덱스 스캔 방식입니다.
  • 조건절에 의해 특정 범위의 인덱스 값을 읽을 때 사용됩니다.
  • 주로 BETWEEN, >, <, IN, LIKE와 같은 조건이 포함될 때 발생합니다.
  • 사용 예시: WHERE age BETWEEN 20 AND 29

✅ 3. INDEX UNIQUE SCAN

  • 인덱스 컬럼이 UNIQUE이고, 조건절이 정확히 하나의 값을 지정할 때 사용됩니다.
  • 항상 최대 하나의 결과만 반환합니다.
  • 성능상 가장 빠른 인덱스 스캔 방식 중 하나입니다.
  • 사용 예시: WHERE id = 123 (id가 유니크한 경우)

🧠 실무 팁

  • 항상 인덱스를 만든다고 좋은 게 아닙니다. 테이블 구조, 조회 조건, 반환 건수 등을 고려해야 합니다.
  • SQL 실행 계획(EXPLAIN PLAN)을 통해 실제 어떤 인덱스 스캔 방식이 사용되는지 확인해보세요.

📌 마무리
인덱스 스캔 방식을 이해하고 있으면, 실행 계획을 해석할 수 있고 쿼리 튜닝 시 효과적으로 방향을 잡을 수 있습니다. 다음 글에서는 "INDEX SKIP SCAN"과 "FAST FULL SCAN" 등 잘 알려지지 않은 스캔 방식도 소개할 예정입니다.

#오라클 #SQL튜닝 #인덱스스캔 #INDEXSCAN #쿼리최적화
#DB성능 #오라클실무 #오라클튜닝 #실무SQL #개발자블로그 #티스토리블로그

반응형
LIST

성능이 확 달라집니다: 오라클 SQL 힌트 실무 적용 가이드

Posted by heoncode
2025. 4. 7. 17:17 오라클 실무 쿼리 튜닝
반응형
SMALL

성능이 확 달라집니다: 오라클 SQL 힌트 실무 적용 가이드

오라클에서 SQL 성능을 조절하고 싶을 때 가장 직접적으로 개입할 수 있는 방법이 힌트(HINT) 입니다.
이번 글에서는 실무에서 자주 쓰는 힌트 종류와 사용법을 예제와 함께 정리해볼게요.


✅ 힌트란?

오라클 옵티마이저는 SQL을 자동으로 최적화하지만,
때로는 개발자가 직접 개입해서 더 나은 실행 계획을 유도할 수 있습니다.

이때 사용하는 것이 바로 힌트(HINT)입니다.
힌트는 주석처럼 보이지만 실제로는 옵티마이저에게 명령을 전달하는 강력한 도구입니다.


✅ 힌트 기본 문법

SELECT /*+ 힌트명 */ 컬럼명 FROM 테이블명;

예시:

SELECT /*+ INDEX(emp emp_name_idx) */ name  
FROM emp  
WHERE name = 'John';

✅ 실무에서 자주 쓰는 힌트 정리

  • FULL(table)
    → 테이블을 무조건 Full Scan 하도록 강제
    SELECT /*+ FULL(emp) */ * FROM emp

  • INDEX(table index_name)
    → 특정 인덱스를 사용하도록 강제
    SELECT /*+ INDEX(emp emp_name_idx) */ name FROM emp WHERE name = 'John'

  • NO_INDEX(table index_name)
    → 특정 인덱스를 사용하지 않도록 강제

  • USE_NL(table)
    → Nested Loop Join을 사용하도록 유도
    SELECT /*+ USE_NL(emp dept) */ ...

  • LEADING(table)
    → 조인 순서를 지정
    SELECT /*+ LEADING(emp dept) */ ...

  • PARALLEL(table, degree)
    → 병렬 처리 적용 (주의: 서버 자원 고려)


🧠 실무 팁

  • 힌트는 남용하면 역효과가 날 수 있습니다.
  • 옵티마이저가 힌트를 무시할 수도 있기 때문에, 실행 계획을 꼭 확인하세요.
  • 힌트는 임시 조치 또는 특정 상황에서 성능을 개선할 때 전략적으로 사용해야 합니다.

📌 마무리

힌트는 고성능 SQL을 작성할 수 있는 강력한 도구입니다.
하지만 제대로 이해하지 않고 사용할 경우 성능 저하를 부를 수도 있습니다.
항상 EXPLAIN PLAN 또는 AUTOTRACE 등을 통해 실행 계획을 확인하세요!

#오라클 #SQL힌트 #쿼리성능 #쿼리튜닝 #오라클실무
#FULLSCAN #INDEX힌트 #실행계획 #오라클튜닝 #개발자블로그 #티스토리블로그

오라클, SQL힌트, 오라클힌트, 쿼리성능, SQL튜닝, 쿼리튜닝, FULLSCAN, INDEX힌트, 실행계획, 오라클튜닝, 힌트적용, 옵티마이저, 오라클실무, 개발자블로그, 티스토리블로그

성능이 확 달라집니다: 오라클 SQL 힌트 실무 적용 가이드

오라클에서 SQL 성능을 조절하고 싶을 때 가장 직접적으로 개입할 수 있는 방법이 힌트(HINT) 입니다.
이번 글에서는 실무에서 자주 쓰는 힌트 종류와 사용법을 예제와 함께 정리해볼게요.


✅ 힌트란?

오라클 옵티마이저는 SQL을 자동으로 최적화하지만,
때로는 개발자가 직접 개입해서 더 나은 실행 계획을 유도할 수 있습니다.

이때 사용하는 것이 바로 힌트(HINT)입니다.
힌트는 주석처럼 보이지만 실제로는 옵티마이저에게 명령을 전달하는 강력한 도구입니다.


✅ 힌트 기본 문법

SELECT /*+ 힌트명 */ 컬럼명 FROM 테이블명;

예시:

SELECT /*+ INDEX(emp emp_name_idx) */ name  
FROM emp  
WHERE name = 'John';

✅ 실무에서 자주 쓰는 힌트 정리

  • FULL(table)
    → 테이블을 무조건 Full Scan 하도록 강제
    SELECT /*+ FULL(emp) */ * FROM emp

  • INDEX(table index_name)
    → 특정 인덱스를 사용하도록 강제
    SELECT /*+ INDEX(emp emp_name_idx) */ name FROM emp WHERE name = 'John'

  • NO_INDEX(table index_name)
    → 특정 인덱스를 사용하지 않도록 강제

  • USE_NL(table)
    → Nested Loop Join을 사용하도록 유도
    SELECT /*+ USE_NL(emp dept) */ ...

  • LEADING(table)
    → 조인 순서를 지정
    SELECT /*+ LEADING(emp dept) */ ...

  • PARALLEL(table, degree)
    → 병렬 처리 적용 (주의: 서버 자원 고려)


🧠 실무 팁

  • 힌트는 남용하면 역효과가 날 수 있습니다.
  • 옵티마이저가 힌트를 무시할 수도 있기 때문에, 실행 계획을 꼭 확인하세요.
  • 힌트는 임시 조치 또는 특정 상황에서 성능을 개선할 때 전략적으로 사용해야 합니다.

📌 마무리

힌트는 고성능 SQL을 작성할 수 있는 강력한 도구입니다.
하지만 제대로 이해하지 않고 사용할 경우 성능 저하를 부를 수도 있습니다.
항상 EXPLAIN PLAN 또는 AUTOTRACE 등을 통해 실행 계획을 확인하세요!

#오라클 #SQL힌트 #쿼리성능 #쿼리튜닝 #오라클실무
#FULLSCAN #INDEX힌트 #실행계획 #오라클튜닝 #개발자블로그 #티스토리블로그

반응형
LIST