캐릭터 셋(character set)은 정보를 표현하기 위한 글자 집합이다. 오라클 데이터베이스는 데이터베이스를 생성할 때 데이터베이스 캐릭터 셋과 내셔널 캐릭터 셋을 지정한다. 내셔널 캐릭터 셋은 데이터베이스 캐릭터 셋이 KO16KSC5601인 경우는 확장 한글, KO16MSWIN949인 경우에는 다국어를 지원하기 위해 사용한다.
-- 1 SELECT * FROM nls_database_parameters WHERE parameter IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET'); PARAMETER VALUE ---------------------- --------- NLS_CHARACTERSET AL32UTF8 NLS_NCHAR_CHARACTERSET AL16UTF16 2 개의 행이 선택되었습니다.
오라클 데이터베이스는 내셔널 캐릭터 셋을 사용할 수 있는 NCHAR, NVARCHAR2, NCLOB 등의 데이터 타입을 제공한다. 테스트를 위해 아래와 같이 테이블을 생성하자. t1 테이블의 c1 칼럼은 VARCHAR2, c2 칼럼은 NVARCHAR2 타입으로 생성했다.
-- 2 DROP TABLE t1 PURGE; CREATE TABLE t1 (c1 VARCHAR2(10), c2 NVARCHAR2(10)); CREATE INDEX t1_x1 ON t1 (c1); CREATE INDEX t1_x2 ON t1 (c2); INSERT INTO t1 VALUES ('A' , 'A' ); INSERT INTO t1 VALUES ('가', '가'); COMMIT;
AL16UTF16 내셔널 캐릭터 셋은 영숫자 저장에 2바이트를 사용하므로 저장 공간 상의 이점이 없다.
-- 3 SELECT a.*, DUMP (a.c1, 1016) AS c1d, DUMP (a.c2, 1016) AS c2d FROM t1 a; C1 C2 C1D C2D -- -- ------------------------------------------- ---------------------------------------- A A Typ=1 Len=1 CharacterSet=AL32UTF8: 41 Typ=1 Len=2 CharacterSet=AL16UTF16: 0,41 가 가 Typ=1 Len=3 CharacterSet=AL32UTF8: ea,b0,80 Typ=1 Len=2 CharacterSet=AL16UTF16: ac,0 2 개의 행이 선택되었습니다.
VARCAHR2 타입과 NVARCHAR2 타입을 비교하면 VARCAHR2 타입이 NVARCHAR2 타입으로 변환된다. 이로 인해 쿼리의 성능이 저하될 수 있다. 아래 쿼리는 리터럴을 사용했다. 리터럴을 사용한 경우에는 성능 저하가 발생하지 않는다. 쿼리 4-2에서 리터럴이 NVARCHAR2 타입으로 변환된 것을 확인할 수 있다. 2
-- 4-1 SELECT * FROM t1 WHERE c1 = 'A'; --------------------------------------------- | Id | Operation | Name | --------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | |* 2 | INDEX RANGE SCAN | T1_X1 | --------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("C1"='A') -- 4-2 SELECT * FROM t1 WHERE c2 = 'A'; --------------------------------------------- | Id | Operation | Name | --------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | |* 2 | INDEX RANGE SCAN | T1_X2 | --------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("C2"=U'A')
-- 5-1 SELECT /*+ LEADING(A) USE_NL(B) */ * FROM t1 a, t1 b WHERE b.c2 = a.c1; ---------------------------------------------- | Id | Operation | Name | ---------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | NESTED LOOPS | | | 3 | TABLE ACCESS FULL | T1 | |* 4 | INDEX RANGE SCAN | T1_X2 | | 5 | TABLE ACCESS BY INDEX ROWID| T1 | ---------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("B"."C2"=SYS_OP_C2C("A"."C1")) -- 5-2 SELECT /*+ LEADING(A) USE_NL(B) */ * FROM t1 a, t1 b WHERE b.c1 = a.c2; ----------------------------------- | Id | Operation | Name | ----------------------------------- | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | TABLE ACCESS FULL| T1 | |* 3 | TABLE ACCESS FULL| T1 | ----------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - filter(SYS_OP_C2C("B"."C1")="A"."C2")
-- 6 DROP TABLE t1 PURGE; CREATE TABLE t1 ( c1 VARCHAR2(100), c2 VARCHAR2(100), c3 VARCHAR2(100) , c4 NVARCHAR2(100), c5 NVARCHAR2(100), c6 NVARCHAR2(100)); INSERT INTO t1 SELECT LPAD ('X', 16, 'X') || LPAD (ROWNUM, 3, '0') AS c1 , LPAD ('X', 16, 'X') || LPAD (ROWNUM, 3, '0') AS c2 FROM XMLTABLE ('1 to 300'); COMMIT;
아래는 DBMS_STATS 패키지로 수집한 통계 정보 결과다. NVARCHAR2 타입의 c2 칼럼에 부정확한 통계 정보가 생성된 것을 확인할 수 있다. 해당 패키지는 히스토그램 생성시 선두 32바이트를 사용하지만, NVARCHAR2 타입의 경우 영숫자가 2바이트를 사용하므로 16자리만 사용할 수 있는 것이 원인이다.
-- 7 EXEC DBMS_STATS.GATHER_TABLE_STATS ('TUNA', 'T1', method_opt => 'FOR ALL COLUMNS'); SELECT column_name, data_type, data_length, num_distinct, density, histogram, num_buckets , UTL_RAW.CAST_TO_VARCHAR2 (low_value) AS low_value FROM user_tab_columns WHERE table_name = 'T1'; COLUMN_NAME DATA_TYPE DATA_LENGTH NUM_DISTINCT DENSITY HISTOGRAM NUM_BUCKETS LOW_VALUE ----------- --------- ----------- ------------ ------------ --------------- ----------- -------------------------------- C1 VARCHAR2 100 300 0.0033333333 HEIGHT BALANCED 75 XXXXXXXXXXXXXXXX001 C2 NVARCHAR2 200 300 0.0016666667 FREQUENCY 1 X X X X X X X X X X X X X X X X 2 개의 행이 선택되었습니다.
-- 8 ANALYZE TABLE t1 COMPUTE STATISTICS; SELECT column_name, data_type, data_length, num_distinct, density, histogram, num_buckets , UTL_RAW.CAST_TO_VARCHAR2 (low_value) AS low_value FROM user_tab_columns WHERE table_name = 'T1'; COLUMN_NAME DATA_TYPE DATA_LENGTH NUM_DISTINCT DENSITY HISTOGRAM NUM_BUCKETS LOW_VALUE ----------- --------- ----------- ------------ ----------- --------- ----------- -------------------------------- C1 VARCHAR2 100 300 0.003333333 NONE 1 XXXXXXXXXXXXXXXX001 C2 NVARCHAR2 200 1 1 NONE 1 X X X X X X X X X X X X X X X X 6 개의 행이 선택되었습니다.
VARCHAR 타입과 NVARCHAR 타입을 함께 사용하는 경우 암시적 테이터 변환에 주의해야 하며, NVARCHAR 타입에 저장될 코드는 선두 중복 값이 없도록 설계할 필요가 있다.
'Oracle > Administration' 카테고리의 다른 글
CHAR vs VARCHAR2 (0) | 2019.01.01 |
---|---|
Intra-block row chaining (0) | 2018.12.28 |
PK 제약조건과 NOT NULL 제약조건 (0) | 2018.12.27 |
오브젝트 명의 고유성 (0) | 2018.11.15 |
인덱스 생성 제약 (0) | 2018.10.19 |