MySQL CONCAT_WS — 구분자 지정 문자열 연결 완전 정리
날짜를 년-월-일 형태로 조립하거나, 주소를 시/구/동처럼 슬래시로 이어붙이고 싶을 때가 있다. 이럴 때 CONCAT으로 하나하나 구분자를 끼워넣는 대신, CONCAT_WS(Concat With Separator) 하나로 훨씬 간결하게 해결할 수 있다.
| CONCAT_WS('/', '2025', '01', '01') |
|---|
| 2025/01/01 |
첫 번째 인자 '/'는 결과에 그대로 출력되는 값이 아니라, 뒤에 오는 문자열들 사이사이에 끼워 넣을 구분자로 소비된다. 그래서 실제 연결 대상은 '2025', '01', '01' 세 개뿐이다.
'...'로 감싼 고정 텍스트 값CONCAT_WS(구분자, 값1, 값2, 값3, ...) 형태로 쓴다. 여기서 핵심은 첫 번째 자리는 "연결될 값"이 아니라 "연결에 쓸 도구"라는 점이다. 즉 함수 이름 자체가 "With Separator(구분자와 함께)"라는 뜻을 담고 있어서, 인자를 몇 개 넣든 첫 번째는 항상 구분자 역할로 소비되고 나머지가 실제 연결 대상이 된다.
일반 CONCAT이라면 CONCAT('2025', '/', '01', '/', '01')처럼 구분자를 값 사이사이에 직접 끼워 넣어야 하지만, CONCAT_WS는 그 반복 작업을 함수가 대신 해준다. 인자가 늘어날수록 이 차이는 코드량으로 확연히 드러난다.
구분자는 '/', '-', ',' 같은 한 글자뿐 아니라 ' - '(공백 포함), ' | ', 심지어 '그리고 ' 같은 여러 글자 문자열도 그대로 쓸 수 있다. 구분자 자리도 결국 하나의 문자열 값이기 때문이다.
구분자 자리에 NULL을 넣으면 어떻게 될까? 이 경우 CONCAT_WS 전체 결과가 NULL이 되어버린다. 값 인자의 NULL은 무시하지만, 구분자 자리의 NULL만큼은 예외 없이 전체를 무효화시킨다는 점을 기억해야 한다.
CONCAT_WS의 가장 강력한 특징은 연결 대상 값 중 NULL이 있으면 그 값만 건너뛰고 나머지를 이어붙인다는 점이다. 예를 들어 회원 테이블에서 중간 이름(middle_name)이 비어 있는 사람이 있어도, 이름 조합 쿼리가 깨지지 않고 자연스럽게 이어진다.
주목할 점은 NULL을 건너뛰면서 구분자가 이중으로 찍히지 않는다는 것이다. 단순히 NULL을 빈 문자열로 치환해서 연결했다면 '홍 길동'처럼 공백이 두 번 들어갔겠지만, CONCAT_WS는 애초에 그 항목을 연결 대상에서 제외하기 때문에 구분자도 한 번만 들어간다.
겉보기엔 둘 다 "문자열을 이어붙이는 함수"지만, 구분자 처리 방식과 NULL 처리 방식에서 결정적으로 갈린다.
구분자를 원하면 값 사이사이에 직접 넣어야 한다.
인자 중 하나라도 NULL이면 전체 결과가 NULL이 된다.
연결 대상 중 NULL은 조용히 건너뛴다.
구분자 자체가 NULL일 때만 전체가 NULL이 된다.
즉 CONCAT은 NULL 하나 때문에 쿼리 결과 전체가 사라지는 경우가 흔한데, CONCAT_WS는 이런 사고를 원천적으로 줄여준다. 이름·주소처럼 일부 필드가 비어 있을 수 있는 데이터를 조합할 때 CONCAT_WS가 선호되는 이유가 여기에 있다.
지금까지 예제는 이해를 돕기 위해 고정된 문자열('2025', '01' 등)을 썼지만, 실제 쿼리에서는 테이블의 컬럼명을 인자 자리에 넣는다. 그러면 행(row)마다 다른 값이 조합되어 출력된다.
이 쿼리는 members 테이블의 각 행마다 city, district, dong 세 컬럼 값을 ', '로 이어붙여 full_address라는 별칭(alias) 컬럼으로 반환한다. 만약 특정 회원의 dong 값이 비어 있어도(NULL) 나머지 두 컬럼만으로 자연스럽게 주소가 만들어진다.
CONCAT_WS의 인자는 반드시 문자열일 필요가 없다. 정수, 날짜(DATE), 시간(TIME) 타입의 컬럼이나 값을 넣어도 MySQL이 내부적으로 문자열로 변환해서 연결한다. 그래서 흩어진 연/월/일 컬럼을 하나의 표기로 합칠 때 자주 쓰인다.
물론 이미 DATE 타입 컬럼 하나로 저장돼 있다면 굳이 CONCAT_WS로 재조합할 필요 없이 DATE_FORMAT을 쓰는 것이 더 적절하다. CONCAT_WS는 어디까지나 이미 여러 컬럼/값으로 나뉘어 있는 조각들을 하나의 문자열로 합칠 때 진가를 발휘한다.
이름이 비슷해 보이는 GROUP_CONCAT과 CONCAT_WS는 완전히 다른 함수다. CONCAT_WS는 한 행(row) 안에서 여러 컬럼(가로 방향)을 하나로 합치는 함수이고, GROUP_CONCAT은 여러 행(세로 방향)에 걸친 같은 컬럼 값들을 그룹 단위로 하나의 문자열로 합치는 집계 함수다.
| 함수 | 연결 방향 | 용도 |
|---|---|---|
| CONCAT_WS | 가로 (같은 행의 여러 컬럼) | 이름·주소·날짜처럼 한 행 안의 조각을 합칠 때 |
| GROUP_CONCAT | 세로 (여러 행의 같은 컬럼) | 한 그룹에 속한 여러 행의 값을 콤마로 나열할 때 |
두 함수를 헷갈려서 GROUP_CONCAT이 필요한 자리에 CONCAT_WS를 쓰면 애초에 여러 행을 하나로 묶는 동작 자체가 일어나지 않아 원하는 결과를 얻을 수 없다.
CONCAT_WS는 NULL을 건너뛰지만, 때로는 건너뛰는 대신 '미입력'처럼 기본값으로 채워서 보여주고 싶을 수 있다. 이럴 땐 CONCAT_WS 자체 기능이 아니라 IFNULL이나 COALESCE로 각 인자를 감싸서 처리한다.
IFNULL(middle_name, '(없음)')처럼 감싸면 빈 값 대신 안내 문구가 들어감- 빈 값을 자연스럽게 생략해도 되면 → CONCAT_WS 기본 동작 그대로 사용
- 빈 값도 사용자에게 "비어 있음"을 알려야 하면 → IFNULL/COALESCE로 감싸기
| 실수 | 증상 | 해결 |
|---|---|---|
| 구분자 자리에 실수로 값을 넣음 (인자 순서 착각) | 첫 번째 값이 사라지고 나머지 값 사이에 이상하게 끼어 있음 | 첫 인자는 항상 구분자, 두 번째 인자부터가 실제 연결 대상임을 확인 |
| 구분자 자리에 NULL이 들어간 컬럼을 넣음 | 결과 전체가 NULL로 나옴 | 구분자는 고정 리터럴을 쓰거나 IFNULL로 기본값 처리 |
| CONCAT과 동작이 같다고 착각 | NULL이 섞인 데이터에서 CONCAT은 결과가 통째로 NULL인데 CONCAT_WS는 정상 출력되어 혼란 | NULL 안전성이 필요하면 처음부터 CONCAT_WS 선택 |
| 여러 행을 합치고 싶은데 CONCAT_WS를 씀 | 행 단위 결과만 나오고 그룹 전체가 하나로 안 합쳐짐 | 여러 행을 합칠 땐 GROUP_CONCAT 사용 |
| 구분자를 빈 문자열로 두고 NULL 무시 효과를 기대 안 함 | 단순 CONCAT처럼 쓰다가 NULL 무시 이점을 놓침 | NULL이 섞일 가능성이 있는 컬럼 조합엔 항상 CONCAT_WS 우선 고려 |
핵심 한 줄 요약
Tags
'php' 카테고리의 다른 글
| MySQL DATEDIFF · TIMEDIFF — 날짜·시간 차이 완전 정리 (0) | 2026.06.28 |
|---|---|
| MySQL ADDDATE · SUBDATE — 날짜 더하기·빼기 완전 정리 (0) | 2026.06.27 |
| MySQL ELT · LOCATE · INSTR — 위치·값 찾기 완전 정리 (0) | 2026.06.27 |
| MySQL SUBSTRING · SPACE · SUBSTRING_INDEX — 문자열 추출 완전 정리 (0) | 2026.06.27 |
| MySQL PAD · TRIM — 패딩·공백 제거 완전 정리 (0) | 2026.06.27 |
왕진 블로그

댓글