2014년 5월 13일 화요일

암시적 형변환에서 데이터 형식 우선 순위

형식이 다른 두 데이터를 연산할 경우 암시적 변환이 일어납니다.
간단한 예를 한번 봅시다.
DECLARE @A CHAR(1)
DECLARE @B INT

SET @A = '1'
SET @B = 1

SELECT @A + @B
결과는 '2' 입니다. CHAR형식이 INT로 암시적 변환이 일어난 후 연산이 된 것입니다. 그러면...
DECLARE @A CHAR(1)
DECLARE @B INT

SET @A = 'A'
SET @B = 1

SELECT @A + @B
이것은 어떤 결과가 나올까요? A1??
메시지 245, 수준 16, 상태 1, 줄 7
varchar 값 'A'을(를) 데이터 형식 int(으)로 변환하지 못했습니다.
이런 에러가 나는 군요. 그러면 두개의 다른 데이터 형식을 연산할 때 어떤 순서로 변환이 일어날까요? MSDN에 의하면 다음 순서로 일어납니다.
사용자 정의 데이터 형식(가장 높음)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar(nvarchar(max) 포함)
nchar
varchar(varchar(max) 포함)
char
varbinary(varbinary(max) 포함)
binary(가장 낮음)
예제와 함께 살펴보면 char 형식은 우선순위가 28위 int는 16위 입니다. 그래서 두 형식을 연산하면 int로 암시적 변환이 됩니다. char형식의 변수에 '1', '2' 같은 숫자가 들어가 있다면 문제없이 변환이 되겠지만 'a', 'b' 등 문자가 들어가 있으면 int로 변환을 하려다가 에러가 발생하게 됩니다. 그런데 만약
DECLARE @A CHAR(1)
DECLARE @B INT

SET @A = 'A'
SET @B = 1

SELECT @A + @B
이 예제에서 'A1' 이라는 결과를 얻고 싶을때는 어떻게 해야 할까요? 그럴때는 CONVERT나 CAST 함수를 통해서 명시적으로 변환을 해야 합니다.
DECLARE @A CHAR(1)
DECLARE @B INT

SET @A = 'A'
SET @B = 1

SELECT @A + CONVERT(CHAR(1), @B)
이렇게요... 암시적 변환으로 인해 원치 않는 데이터가 나올 수도 있지만 암시적 변환은 인덱스 사용에도 문제를 일으킵니다. 아래 그림은 CounterTemp라는 테이블의 내용을 막강 프로시져 SP_HELPX를 통해 살펴본 모습입니다.
CounterTemp는 varchar형식의 CounterID열에 Clustered Index를 가진 테이블입니다. 여기에 CounterID가 100인 데이터를 쿼리해보겠습니다.
SELECT * FROM CounterTemp WHERE CounterID = 100
그리고 실행 계획을 보니...
어찌된 일인지 Clustered Index Scan을 했습니다. 무엇때문에 그럴까요? 조건자를 보니까 CONVERT_IMPLICT라는게 보입니다. VARCHAR와 INT의 형변환 경합에서 INT가 우선이므로 컬럼과 조건이 모두 암시적 형변환이 일어난 것입니다. 형변환 때문에 인덱스를 사용하지 못하고 Scan이 되어 버린 것이지요. 그럼 어떻게 해야 인덱스를 정상적으로 사용할까요? 인덱스 컬럼의 데이터 형식과 조건의 데이터 형식을 맞추어야 합니다.
DECLARE @A VARCHAR(10)

SET @A = '100'

SELECT * FROM CounterTemp WHERE CounterID = @A
이렇게 쿼리 한 후 실행계획을 다시 보면 정상적으로 인덱스를 Seek한것을 볼 수 있습니다.
참고자료 데이터 형식 우선 순위(Transact-SQL)

댓글 없음:

댓글 쓰기