본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

MySQL CONCAT_WS — 구분자 지정 문자열 연결 완전 정리

by 왕진 2026. 6. 27.
반응형

 

 

Database · MySQL

MySQL CONCAT_WS — 구분자 지정 문자열 연결 완전 정리

첫 번째 인자를 구분자로 삼아 나머지 문자열을 이어붙이는 함수, CONCAT과 무엇이 다른가
시작 오늘 분석할 코드

날짜를 년-월-일 형태로 조립하거나, 주소를 시/구/동처럼 슬래시로 이어붙이고 싶을 때가 있다. 이럴 때 CONCAT으로 하나하나 구분자를 끼워넣는 대신, CONCAT_WS(Concat With Separator) 하나로 훨씬 간결하게 해결할 수 있다.

SELECT CONCAT_WS('/', '2025', '01', '01'); -- 구분자를 지정하여 그것으로 연결함
실제 결과 미리보기
CONCAT_WS('/', '2025', '01', '01')
2025/01/01

첫 번째 인자 '/'는 결과에 그대로 출력되는 값이 아니라, 뒤에 오는 문자열들 사이사이에 끼워 넣을 구분자로 소비된다. 그래서 실제 연결 대상은 '2025', '01', '01' 세 개뿐이다.

용어 용어 정리
CONCAT_WSConcat With Separator. 구분자를 지정해 여러 문자열을 하나로 잇는 MySQL 문자열 함수
구분자(separator)함수의 첫 번째 인자. 연결될 문자열 사이사이에 반복해서 삽입되는 기준 문자
CONCAT구분자 개념 없이 인자를 순서대로 그냥 이어붙이기만 하는 원조 문자열 연결 함수
NULL 무시CONCAT_WS는 인자 중 NULL을 건너뛰고 남은 값들만 연결한다. CONCAT과 가장 큰 차이점
가변 인자구분자를 제외한 나머지 인자 개수는 2개든 20개든 자유롭게 넣을 수 있음
문자열 리터럴SQL에서 작은따옴표 '...'로 감싼 고정 텍스트 값
컬럼 참조리터럴 대신 테이블의 컬럼명을 인자로 넣으면 행마다 다른 값이 연결됨
암묵적 형변환숫자·날짜 타입 인자도 CONCAT_WS 안에서는 자동으로 문자열로 바뀌어 연결됨

1 CONCAT_WS의 기본 문법과 동작 원리
왜 첫 번째 인자가 특별한가?

CONCAT_WS(구분자, 값1, 값2, 값3, ...) 형태로 쓴다. 여기서 핵심은 첫 번째 자리는 "연결될 값"이 아니라 "연결에 쓸 도구"라는 점이다. 즉 함수 이름 자체가 "With Separator(구분자와 함께)"라는 뜻을 담고 있어서, 인자를 몇 개 넣든 첫 번째는 항상 구분자 역할로 소비되고 나머지가 실제 연결 대상이 된다.

일반 CONCAT이라면 CONCAT('2025', '/', '01', '/', '01')처럼 구분자를 값 사이사이에 직접 끼워 넣어야 하지만, CONCAT_WS는 그 반복 작업을 함수가 대신 해준다. 인자가 늘어날수록 이 차이는 코드량으로 확연히 드러난다.

※ 인자 순서는 "구분자 → 연결할 값들"로 고정. 순서를 바꿔서 값을 첫 자리에 넣으면 그 값이 구분자로 오작동한다.

2 구분자는 어떤 값이든 올 수 있다
한 글자가 아니어도 된다

구분자는 '/', '-', ',' 같은 한 글자뿐 아니라 ' - '(공백 포함), ' | ', 심지어 '그리고 ' 같은 여러 글자 문자열도 그대로 쓸 수 있다. 구분자 자리도 결국 하나의 문자열 값이기 때문이다.

SELECT CONCAT_WS(' - ', '서울특별시', '강남구', '역삼동'); -- 결과: 서울특별시 - 강남구 - 역삼동

구분자 자리에 NULL을 넣으면 어떻게 될까? 이 경우 CONCAT_WS 전체 결과가 NULL이 되어버린다. 값 인자의 NULL은 무시하지만, 구분자 자리의 NULL만큼은 예외 없이 전체를 무효화시킨다는 점을 기억해야 한다.

※ 구분자 인자에 NULL이 들어가면 결과 전체가 NULL이다. "값 NULL은 무시"라는 규칙과 혼동하지 말 것.

3 NULL 값은 자동으로 무시된다
왜 이 특징이 실무에서 중요한가?

CONCAT_WS의 가장 강력한 특징은 연결 대상 값 중 NULL이 있으면 그 값만 건너뛰고 나머지를 이어붙인다는 점이다. 예를 들어 회원 테이블에서 중간 이름(middle_name)이 비어 있는 사람이 있어도, 이름 조합 쿼리가 깨지지 않고 자연스럽게 이어진다.

SELECT CONCAT_WS(' ', '홍', NULL, '길동'); -- 결과: '홍 길동' (NULL 자리는 건너뛰고, 구분자도 중복되지 않는다)

주목할 점은 NULL을 건너뛰면서 구분자가 이중으로 찍히지 않는다는 것이다. 단순히 NULL을 빈 문자열로 치환해서 연결했다면 '홍 길동'처럼 공백이 두 번 들어갔겠지만, CONCAT_WS는 애초에 그 항목을 연결 대상에서 제외하기 때문에 구분자도 한 번만 들어간다.

※ 이 동작 덕분에 "선택적 필드"를 조합할 때 IFNULL이나 CASE로 일일이 방어 코드를 짤 필요가 없다.

 

반응형

 

4 CONCAT과 CONCAT_WS, 실제로 뭐가 다른가
비교로 확인하는 차이점

겉보기엔 둘 다 "문자열을 이어붙이는 함수"지만, 구분자 처리 방식과 NULL 처리 방식에서 결정적으로 갈린다.

CONCAT
인자를 순서대로 단순 연결만 한다.
구분자를 원하면 값 사이사이에 직접 넣어야 한다.
인자 중 하나라도 NULL이면 전체 결과가 NULL이 된다.
CONCAT_WS
첫 인자를 구분자로 삼아 자동으로 끼워 넣는다.
연결 대상 중 NULL은 조용히 건너뛴다.
구분자 자체가 NULL일 때만 전체가 NULL이 된다.
SELECT CONCAT('홍', NULL, '길동'); -- 결과: NULL SELECT CONCAT_WS('', '홍', NULL, '길동'); -- 결과: '홍길동'

즉 CONCAT은 NULL 하나 때문에 쿼리 결과 전체가 사라지는 경우가 흔한데, CONCAT_WS는 이런 사고를 원천적으로 줄여준다. 이름·주소처럼 일부 필드가 비어 있을 수 있는 데이터를 조합할 때 CONCAT_WS가 선호되는 이유가 여기에 있다.


5 테이블 컬럼과 함께 사용하기
실무에서는 리터럴이 아니라 컬럼을 넣는다

지금까지 예제는 이해를 돕기 위해 고정된 문자열('2025', '01' 등)을 썼지만, 실제 쿼리에서는 테이블의 컬럼명을 인자 자리에 넣는다. 그러면 행(row)마다 다른 값이 조합되어 출력된다.

SELECT CONCAT_WS(', ', city, district, dong) AS full_address FROM members;

이 쿼리는 members 테이블의 각 행마다 city, district, dong 세 컬럼 값을 ', '로 이어붙여 full_address라는 별칭(alias) 컬럼으로 반환한다. 만약 특정 회원의 dong 값이 비어 있어도(NULL) 나머지 두 컬럼만으로 자연스럽게 주소가 만들어진다.

-- district가 NULL인 회원 예시 city='서울특별시', district=NULL, dong='역삼동' → 결과: '서울특별시, 역삼동' (콤마가 중복되지 않음)

6 날짜·시간 조합에 활용하기
숫자·날짜 타입도 자동으로 문자열이 된다

CONCAT_WS의 인자는 반드시 문자열일 필요가 없다. 정수, 날짜(DATE), 시간(TIME) 타입의 컬럼이나 값을 넣어도 MySQL이 내부적으로 문자열로 변환해서 연결한다. 그래서 흩어진 연/월/일 컬럼을 하나의 표기로 합칠 때 자주 쓰인다.

SELECT CONCAT_WS('-', YEAR(order_date), MONTH(order_date), DAY(order_date)) AS order_ymd FROM orders;

물론 이미 DATE 타입 컬럼 하나로 저장돼 있다면 굳이 CONCAT_WS로 재조합할 필요 없이 DATE_FORMAT을 쓰는 것이 더 적절하다. CONCAT_WS는 어디까지나 이미 여러 컬럼/값으로 나뉘어 있는 조각들을 하나의 문자열로 합칠 때 진가를 발휘한다.

※ 이미 하나의 DATE/DATETIME 컬럼이 있다면 CONCAT_WS보다 DATE_FORMAT()이 더 정확하고 의도가 분명하다.

7 GROUP_CONCAT과 헷갈리지 않기
"여러 값 연결"이라도 방향이 다르다

이름이 비슷해 보이는 GROUP_CONCAT과 CONCAT_WS는 완전히 다른 함수다. CONCAT_WS는 한 행(row) 안에서 여러 컬럼(가로 방향)을 하나로 합치는 함수이고, GROUP_CONCAT은 여러 행(세로 방향)에 걸친 같은 컬럼 값들을 그룹 단위로 하나의 문자열로 합치는 집계 함수다.

함수 연결 방향 용도
CONCAT_WS 가로 (같은 행의 여러 컬럼) 이름·주소·날짜처럼 한 행 안의 조각을 합칠 때
GROUP_CONCAT 세로 (여러 행의 같은 컬럼) 한 그룹에 속한 여러 행의 값을 콤마로 나열할 때

두 함수를 헷갈려서 GROUP_CONCAT이 필요한 자리에 CONCAT_WS를 쓰면 애초에 여러 행을 하나로 묶는 동작 자체가 일어나지 않아 원하는 결과를 얻을 수 없다.


8 IFNULL·COALESCE와 함께 쓸 때
NULL을 "건너뛰기"가 아니라 "기본값으로 채우기"로 바꾸고 싶다면

CONCAT_WS는 NULL을 건너뛰지만, 때로는 건너뛰는 대신 '미입력'처럼 기본값으로 채워서 보여주고 싶을 수 있다. 이럴 땐 CONCAT_WS 자체 기능이 아니라 IFNULL이나 COALESCE로 각 인자를 감싸서 처리한다.

1
기본 동작 확인CONCAT_WS는 NULL 값을 그냥 생략한다 — 별도 처리 없이도 안전하게 연결됨
2
기본값 표기가 필요하면 IFNULL 추가IFNULL(middle_name, '(없음)')처럼 감싸면 빈 값 대신 안내 문구가 들어감
3
둘을 조합CONCAT_WS(구분자, IFNULL(a,'-'), IFNULL(b,'-'), ...) 형태로 각 인자를 개별 방어
SELECT CONCAT_WS(' ', '홍', IFNULL(middle_name, '(없음)'), '길동') AS full_name FROM members;
선택 기준
  • 빈 값을 자연스럽게 생략해도 되면 → CONCAT_WS 기본 동작 그대로 사용
  • 빈 값도 사용자에게 "비어 있음"을 알려야 하면 → IFNULL/COALESCE로 감싸기

9 자주 하는 실수
실수 증상 해결
구분자 자리에 실수로 값을 넣음 (인자 순서 착각) 첫 번째 값이 사라지고 나머지 값 사이에 이상하게 끼어 있음 첫 인자는 항상 구분자, 두 번째 인자부터가 실제 연결 대상임을 확인
구분자 자리에 NULL이 들어간 컬럼을 넣음 결과 전체가 NULL로 나옴 구분자는 고정 리터럴을 쓰거나 IFNULL로 기본값 처리
CONCAT과 동작이 같다고 착각 NULL이 섞인 데이터에서 CONCAT은 결과가 통째로 NULL인데 CONCAT_WS는 정상 출력되어 혼란 NULL 안전성이 필요하면 처음부터 CONCAT_WS 선택
여러 행을 합치고 싶은데 CONCAT_WS를 씀 행 단위 결과만 나오고 그룹 전체가 하나로 안 합쳐짐 여러 행을 합칠 땐 GROUP_CONCAT 사용
구분자를 빈 문자열로 두고 NULL 무시 효과를 기대 안 함 단순 CONCAT처럼 쓰다가 NULL 무시 이점을 놓침 NULL이 섞일 가능성이 있는 컬럼 조합엔 항상 CONCAT_WS 우선 고려

핵심 한 줄 요약

문법CONCAT_WS(구분자, 값1, 값2, ...) — 첫 인자는 구분자 전용
NULL 처리값 인자의 NULL은 자동 무시, 단 구분자 인자가 NULL이면 전체가 NULL
CONCAT과 차이CONCAT은 NULL 하나로 결과 전체가 사라짐, CONCAT_WS는 그 값만 건너뜀
GROUP_CONCAT과 차이CONCAT_WS는 한 행 내 가로 연결, GROUP_CONCAT은 여러 행 세로 연결
실무 활용이름·주소·날짜 조각처럼 일부가 비어 있을 수 있는 값을 안전하게 합칠 때

Tags

#MySQL #CONCAT_WS #구분자 #문자열연결 #separator #CONCAT #SQL함수 #NULL처리 #문자열함수 #GROUP_CONCAT #SQL #데이터베이스 #DB #웹개발 #티스토리
▼ 티스토리 태그 입력란 복사용
MySQL, CONCAT_WS, 구분자, 문자열연결, separator, CONCAT, SQL함수, NULL처리, 문자열함수, GROUP_CONCAT, SQL, 데이터베이스, DB, 웹개발, 티스토리
반응형

댓글