지난 초급 4편에서는 SELECT INTO로 연습 테이블을 만들고, INSERT를 세 가지 방법으로 확장해 봤습니다. 오늘의 주제는 형변환입니다. 입문 10편에서 CAST를 잠깐 만나긴 했지만, 이번에는 CAST와 CONVERT의 문법 차이, 날짜를 원하는 모양으로 바꾸는 스타일 코드, 문자를 숫자로 바꾸다 쿼리가 통째로 멈추는 함정, 그리고 그 해독제인 TRY_CAST/TRY_CONVERT까지 제대로 정리하겠습니다. 오늘은 처음부터 끝까지 조회만 하므로, BookMart 원본 데이터는 시작할 때도 끝날 때도 그대로입니다.
SQL·MES 전체 글 보기 →
첫 번째 함정 — SQL Server가 몰래 하는 암시적 변환
시나리오부터 시작하겠습니다. 운영팀이 도서 목록을 "제목 (가격원)" 형태의 한 줄 라벨로 뽑아 달라고 합니다. 문자열 연결은 +로 하면 되니, 별생각 없이 이렇게 써 보겠습니다.
USE BookMart;
SELECT Title + N'의 가격은 ' + Price
FROM Book
WHERE BookID = 3;
실행하면 결과 대신 오류가 납니다. 문구는 SQL Server 버전·언어 설정에 따라 조금 다를 수 있습니다.
메시지 245, 수준 16, 상태 1, 줄 1
nvarchar 값 '차근차근 SQL의 가격은 '을(를) 데이터 형식 int(으)로 변환하지 못했습니다.
오류를 읽어 보면 재미있는 점이 보입니다. 우리는 "숫자를 문자로 바꿔서 붙여 줘"라고 기대했는데, SQL Server는 반대로 문자를 int로 바꾸려다 실패했습니다. + 양쪽의 형식이 다르면 SQL Server가 암시적 변환을 시도하는데, 이때 어느 쪽을 바꿀지는 우리 기대가 아니라 데이터 형식 우선순위가 정합니다. int가 nvarchar보다 우선순위가 높아서, 문자 쪽('차근차근 SQL의 가격은 ')을 int로 바꾸려다 멈춘 것입니다.
여기에 성능 함정도 한 줄 덧붙입니다 — WHERE 절에서 열과 비교 값의 형식이 다르면 SQL Server가 행마다 열 쪽을 암시적으로 변환하느라 인덱스를 제대로 활용하지 못할 수 있으니, 변환이 필요하다면 열이 아니라 비교 값 쪽을 맞춰 주는 것이 원칙입니다.
CAST — 표준 문법으로 바로잡기
암시적 변환에 맡기지 말고 우리가 직접, 명시적으로 변환하겠습니다. 가장 기본 도구가 CAST입니다.
CAST(식 AS 데이터형식)
Price를 nvarchar로 바꿔서 라벨을 완성해 보겠습니다. 이번에는 IT 카테고리 전체로 범위를 넓힙니다.
SELECT BookID,
Title + N' (' + CAST(Price AS nvarchar(10)) + N'원)' AS PriceLabel
FROM Book
WHERE CategoryID = 3
ORDER BY BookID;

이번에는 오류 없이 IT 도서 3권의 라벨이 깔끔하게 나왔습니다. "차근차근 SQL (27000원)"처럼요. 27000에 쉼표를 찍고 싶다면 입문 10편에서 본 FORMAT 같은 함수도 있지만, 오늘의 주제는 형변환이니 여기서는 그대로 두겠습니다.
CONVERT — 스타일 코드가 필요할 때
SQL Server에는 같은 일을 하는 도구가 하나 더 있습니다. CONVERT입니다.
CONVERT(데이터형식, 식 [, 스타일])
CAST(Price AS nvarchar(10))과 CONVERT(nvarchar(10), Price)는 결과가 같습니다. 차이는 두 가지입니다. CAST는 표준 SQL이라 다른 DBMS에서도 통하고, CONVERT는 SQL Server 전용이지만 세 번째 인수 스타일 코드를 받을 수 있습니다. 이 스타일 코드가 CONVERT의 존재 이유로, 날짜를 문자로 바꿀 때 출력 모양을 번호 하나로 고르게 해 줍니다.
말보다 화면이 빠릅니다. 이제 Orders의 주문일 하나를 여러 스타일로 동시에 변환해 보겠습니다.
SELECT TOP (3)
OrderID,
OrderDate,
CONVERT(char(8), OrderDate, 112) AS Style112,
CONVERT(char(10), OrderDate, 23) AS Style23,
CONVERT(char(19), OrderDate, 120) AS Style120,
CONVERT(char(10), OrderDate, 102) AS Style102
FROM Orders
ORDER BY OrderID;

같은 OrderDate가 스타일 번호에 따라 전혀 다른 모습으로 나왔습니다. 자주 쓰는 코드만 표로 정리하면 이렇습니다(예시는 주문 1001의 2025-04-02 10:15:00 기준).
스타일모양예
| 112 | yyyymmdd | 20250402 |
| 23 | yyyy-mm-dd | 2025-04-02 |
| 120 | yyyy-mm-dd hh:mi:ss | 2025-04-02 10:15:00 |
| 102 | yyyy.mm.dd | 2025.04.02 |
| 101 | mm/dd/yyyy | 04/02/2025 |
| 103 | dd/mm/yyyy | 02/04/2025 |
112는 파일명이나 일자 키를 만들 때, 120은 로그처럼 초까지 기록할 때, 23은 날짜만 보여줄 때 애용됩니다. 참고로 스타일 코드는 반대 방향, 즉 문자를 날짜로 해석할 때도 같은 번호를 씁니다. CONVERT(date, '20250402', 112)처럼요.
스타일 코드를 실전에서 어떻게 써먹는지 하나만 더 해 보겠습니다. 스타일 120의 앞 7글자만 잘라내면 "2025-04" 같은 연-월 문자열이 되니, 이걸로 월별 주문 건수를 집계할 수 있습니다.
SELECT CONVERT(char(7), OrderDate, 120) AS OrderMonth,
COUNT(*) AS OrderCnt
FROM Orders
GROUP BY CONVERT(char(7), OrderDate, 120)
ORDER BY OrderMonth;

4월 3건, 5월 3건, 6월 3건, 7월 1건 — 취소 주문까지 포함해 전체 10건이 월별로 정리됐습니다. CONVERT의 대상 길이를 char(7)로 지정하면 스타일 120의 결과가 앞에서부터 7글자로 잘리는 성질을 이용한 것입니다.
문자 → 숫자, 진짜 함정 구간
이번에는 반대 방향, 문자를 숫자로 바꿔 보겠습니다. 외부에서 받은 엑셀이나 CSV의 숫자가 텍스트로 들어오는 일은 실무에서 아주 흔한데, 이 구간이 형변환 사고 다발 지역입니다. 사람 눈에는 멀쩡한 숫자로 보이는 '12,000'을 변환해 보겠습니다.
SELECT CAST(N'12,000' AS int);
역시 문구는 버전·언어 설정에 따라 다를 수 있지만, 이런 오류가 납니다.
메시지 245, 수준 16, 상태 1, 줄 1
nvarchar 값 '12,000'을(를) 데이터 형식 int(으)로 변환하지 못했습니다.
범인은 쉼표입니다. int로 가는 길에는 숫자와 부호만 허용되고, 쉼표·통화 기호·공백이 끼어 있으면 그대로 멈춥니다. 쉼표가 문제라는 걸 안다면 CAST(REPLACE(N'12,000', N',', N'') AS int)처럼 먼저 지우고 변환하면 되고, 결과는 12000이 나옵니다. 문제는 수천 행짜리 외부 데이터에서 어느 행이 불량인지 모를 때입니다. 단 한 행만 이상해도 쿼리 전체가 오류로 멈추니까요.
TRY_CAST / TRY_CONVERT — 실패하면 NULL
그래서 SQL Server에는 안전장치가 준비되어 있습니다. TRY_CAST와 TRY_CONVERT는 문법이 CAST·CONVERT와 완전히 같고, 딱 하나 — 변환에 실패하면 오류 대신 NULL을 돌려준다는 점만 다릅니다. 이제 성공하는 값과 실패하는 값을 한 화면에서 비교해 보겠습니다.
SELECT TRY_CAST(N'123' AS int) AS Result1,
TRY_CAST(N'12,000' AS int) AS Result2,
TRY_CAST(N'abc' AS int) AS Result3;

'123'은 123으로 변환됐고, 방금 오류를 냈던 '12,000'과 애초에 숫자가 아닌 'abc'는 오류 없이 NULL이 됐습니다. 쿼리는 끝까지 완주했고요. 날짜도 마찬가지로 검증할 수 있습니다. 달력에 없는 날짜를 넣어 볼까요?
SELECT TRY_CONVERT(date, N'2025-07-10') AS RealDate,
TRY_CONVERT(date, N'2025-13-01') AS FakeDate;

2025-07-10은 정상 변환됐지만, 13월 1일은 세상에 없는 날짜라 NULL이 나왔습니다. 이 성질 덕분에 WHERE TRY_CAST(열 AS int) IS NULL 같은 조건으로 불량 데이터만 골라내는 검증 쿼리를 만들 수 있습니다. 다만 주의할 점 — TRY_ 계열은 실패를 NULL로 조용히 삼키기 때문에, 검증 없이 집계에 바로 쓰면 불량 행이 티 안 나게 빠져나갑니다. "일단 오류만 안 나게" 하는 도구가 아니라, NULL이 나온 행을 반드시 확인하는 습관과 세트로 쓰셔야 합니다.
오늘은 단 한 건의 데이터도 바꾸지 않았으니 따로 정리할 것도 없습니다. BookMart는 시드 데이터 그대로입니다.
오늘의 정리
- CAST(식 AS 형식)는 표준 SQL, CONVERT(형식, 식, 스타일)는 SQL Server 전용 — 기본 변환은 결과가 같고, 스타일 코드가 필요할 때 CONVERT를 씁니다.
- 날짜 스타일 코드는 112=YYYYMMDD, 23=YYYY-MM-DD, 120=YYYY-MM-DD HH:MI:SS 세 가지만 외워도 실무 대부분을 커버합니다.
- 형식이 다른 값끼리 만나면 우선순위 높은 쪽(예: int > nvarchar)으로 암시적 변환이 일어나며, 문자에 쉼표 하나만 섞여 있어도 숫자 변환은 오류로 멈춥니다.
- WHERE 절에서 열 쪽이 암시적 변환되면 인덱스를 활용하지 못할 수 있으므로, 변환은 열이 아니라 비교 값 쪽에 하는 것이 원칙입니다.
- TRY_CAST/TRY_CONVERT는 실패 시 오류 대신 NULL 반환 — 불량 데이터 검증에 유용하지만, NULL로 빠진 행을 확인하는 습관이 필수입니다.
다음 편 예고
형변환으로 값의 "형식"을 다루는 법을 익혔으니, 다음 초급 6편에서는 쿼리의 뼈대인 연산자 총정리와 CASE 표현식을 다룹니다. AND와 OR이 섞였을 때 결과가 조용히 틀어지는 우선순위 문제를 재현해 보고, 조건에 따라 값을 만들어 내는 CASE로 가격대 구간 나누기까지 실습해 보겠습니다.
'SQL · MES' 카테고리의 다른 글
| MSSQL 여러 열로 그룹화하기 — 다중 열 GROUP BY와 COUNT DISTINCT [MSSQL 23] (0) | 2026.07.16 |
|---|---|
| MSSQL SQL 연산자와 CASE 문 — 조건별 값 분류 [MSSQL 22] (0) | 2026.07.16 |
| MSSQL 여러 행 INSERT 방법 — 다중 입력·INSERT SELECT [MSSQL 20] (0) | 2026.07.16 |
| MSSQL UPDATE 사용법 — 여러 조건으로 안전하게 수정하기 [MSSQL 19] (0) | 2026.07.16 |
| MSSQL 제약조건 — NOT NULL·UNIQUE·CHECK·DEFAULT [MSSQL 18] (0) | 2026.07.16 |