Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

DBDBDEEP

2.2.7 자동 형변환 본문

친절한 SQL 튜닝

2.2.7 자동 형변환

Kihwane 2026. 5. 16. 20:10

1. 테이블 생성 (Oracle 기준)

 

CREATE TABLE customer (
    birth_date CHAR(8) NOT NULL,       -- Format: 'YYYYMMDD'
    customer_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name VARCHAR2(50),
    email VARCHAR2(100)
);

-- 생년월일 인덱스
CREATE INDEX idx_customer_birth_date ON customer(birth_date);

 

2. 랜덤 데이터 100만 건 삽입

 

Oracle에서는 DBMS_RANDOM과 CONNECT BY를 활용해 데이터를 생성할 수 있습니다.

 

BEGIN
  FOR i IN 1..100 LOOP
    INSERT /*+ APPEND */ INTO customer (birth_date, name, email)
    SELECT
      TO_CHAR(DATE '1970-01-01' + TRUNC(DBMS_RANDOM.VALUE(0, 13150)), 'YYYYMMDD'),
      'Customer' || LPAD(TO_CHAR(TRUNC(DBMS_RANDOM.VALUE(1, 100000))), 5, '0'),
      'user' || LPAD(TO_CHAR(TRUNC(DBMS_RANDOM.VALUE(1, 1000000))), 6, '0') || '@test.com'
    FROM dual
    CONNECT BY LEVEL <= 10000;  -- 100 * 10,000 = 1,000,000 rows
    COMMIT;
  END LOOP;
END;
/

 

 

3. 생년월일이 선두 컬럼인 인덱스 존재

 

다음과 같이 조회했을 나오는 실행계획
SQL> select * from customer
        where birth_date=19950627;

71 rows selected.

Total elapsed time 00:00:00.096286
 

SQL ID: apv1tfwtpsww9
Child number: 112
Plan hash value: 3696328097


Execution Plan
--------------------------------------------------------------------------------------------------------
1  TABLE ACCESS (FULL): CUSTOMER (Cost:3150, %%CPU:0, Rows:80)





Predicate Information
--------------------------------------------------------------------------------------------------------
1 - filter: ("CUSTOMER"."BIRTH_DATE" = 19950627) (0.000)





Note
--------------------------------------------------------------------------------------------------------
1 - dynamic sampling used for this table (98 blocks)





NAME                                VALUE
------------------------------ ----------
db block gets                         489
consistent gets                      7444
physical reads                       7299
redo size                              76
sorts (disk)                            0
sorts (memory)                          2
rows processed                         71

 

인덱스가 있음에도 불구하고 TABLE ACCESS (FULL) 스캔을 했다.

이유는 무엇일까?

해당 테이블의 birth_date CHAR 형식이라 문자형으로 저장되어 있고, 인덱스도 문자열을 기준으로 정렬되어 있다.

그러나 조회 쿼리에서는 조건절에 상수형으로 주었다.

 

자동 형변환이 일어나게 것이다.
* 오라클에서 숫자형과 문자형이 만나면 숫자형이 이긴다
* 하지만 like 연산자일때는 다르다. Like 자체가 문자열 비교 함수라, 상수가 들어오면 문자열로 바뀌게 된다.

 

SQL> select * from customer where birth_date='19950627';

 

71 rows selected.
Total elapsed time 00:00:00.012057


SQL ID: a7rkhtfvfs320
Child number: 131
Plan hash value: 3497942325


Execution Plan
--------------------------------------------------------------------------------------------------------
   1  TABLE ACCESS (ROWID): CUSTOMER (Cost:84, %%CPU:0, Rows:80) 00
   2    INDEX (RANGE SCAN): IDX_CUSTOMER_BIRTH_DATE (Cost:3, %%CPU:0, Rows:80)




Predicate Information
--------------------------------------------------------------------------------------------------------
   2 - access: ("CUSTOMER"."BIRTH_DATE" = '19950627') (0.000)




Note
--------------------------------------------------------------------------------------------------------
   2 - dynamic sampling used for this table (98 blocks)

 

위와 같이 조건절을 문자형으로 주게 되면 인덱스 사용에 전혀 문제가 없다.

속도도 9 빨라졌음을 있고 INDEX RANGE SCAN 사용하는 확인할 있다.

 

SQL 성능은 형변환을 줄이는 데서 나오는 것이 아니라 '블록 I/O 줄일 있느냐 없느냐' 에서 나온다.

위에서 확인했듯 개발자가 형변환 함수를 생략해도 옵티마이저가 자동으로 생성한다.

'친절한 SQL 튜닝' 카테고리의 다른 글

3.1.2 Index Clustering Factor  (0) 2026.05.16
2.3 인덱스 확장기능  (0) 2026.05.16
2.2.3 인덱스 사용 조건  (0) 2026.05.16
OR Expansion  (0) 2026.05.16
2.1.5 결합 인덱스 구조와 탐색  (0) 2026.05.16