MariaDB / 함수 / FIND_IN_SET / 특정 문자열의 위치를 출력하는 함수
MariaDB의 FIND_IN_SET(찾을 값, 쉼표 목록)은 쉼표로 구분된 항목 중 값이 처음 나타나는 순번을 1부터 반환합니다. 찾지 못하면 0, 인수에 NULL이 있으면 NULL이며, 문자열 안의 문자 위치를 찾는 함수와는 다릅니다.
항목의 순번과 검색 실패
테이블 없이 다음 SELECT를 실행하면 반환값을 비교할 수 있습니다. 두 번째 인수 전체가 하나의 문자열이고, 그 안의 쉼표가 항목을 나눕니다. FIND_IN_SET 공식 문서에서 인수와 반환 조건을 확인할 수 있습니다.
SELECT FIND_IN_SET('B', 'AA,B,CC,D') AS found_position,
FIND_IN_SET('B', 'A,B,B,D') AS first_match,
FIND_IN_SET('Z', 'A,B,C,D') AS not_found,
FIND_IN_SET('B', '') AS empty_list,
FIND_IN_SET(NULL, 'A,B') AS missing_pattern,
FIND_IN_SET('B', NULL) AS missing_list;
결과는 2·2·0·0·NULL·NULL입니다. 첫 예제의 B 앞에는 AA와 쉼표가 있지만 결과는 문자 위치 4가 아니라 두 번째 항목이라는 뜻의 2입니다. 항목이 중복되면 첫 일치만 반환합니다. 찾는 값이 목록에 포함되는지 조건으로 쓰려면 결과가 0보다 큰지 검사하면 의도가 분명합니다.
공백·대소문자·쉼표가 있는 값
SELECT FIND_IN_SET('B', 'A, B,C') AS without_space,
FIND_IN_SET(' B', 'A, B,C') AS with_space,
FIND_IN_SET('B', 'AB,BC') AS partial_match,
FIND_IN_SET('a,b', 'a,b,c') AS comma_in_pattern;
SELECT FIND_IN_SET(_utf8mb4'b' COLLATE utf8mb4_general_ci,
_utf8mb4'A,B,C' COLLATE utf8mb4_general_ci) AS ignore_case,
FIND_IN_SET(_utf8mb4'b' COLLATE utf8mb4_bin,
_utf8mb4'A,B,C' COLLATE utf8mb4_bin) AS distinguish_case;
첫 조회는 0·2·0·0입니다. 쉼표 뒤 공백을 자동 제거하지 않으며 AB의 일부인 B에도 일치하지 않습니다. 찾을 값 자체에 쉼표가 있으면 항목 경계를 표현할 수 없으므로 이 함수로 올바르게 검색할 수 없습니다. 큰따옴표 안의 쉼표를 한 필드로 보는 CSV 파일 파서로 사용해서도 안 됩니다.
둘째 조회는 2·0입니다. 문자열 비교 규칙인 콜레이션(collation)을 대소문자 비구분과 바이너리 구분으로 각각 명시했습니다. 환경에 따라 달라질 수 있는 비교를 “항상 대소문자를 구별한다” 또는 “항상 무시한다”로 일반화하지 마세요. 공백 제거가 필요하더라도 목록 전체에서 모든 공백을 지우면 값 내부의 의미 있는 공백까지 손상될 수 있습니다.
기존 쉼표 목록 열에서 포함 여부 조회
다음은 실습용 learning 데이터베이스에서 CREATE·INSERT·SELECT 권한으로 실행하는 전체 예제입니다. tag_list_demo가 있으면 다른 실습 이름을 사용합니다. 여기서는 기존 열이 쉼표 목록으로 저장된 경우를 읽기만 합니다.
USE learning;
CREATE TABLE tag_list_demo (
id INT PRIMARY KEY,
tags VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL
);
INSERT INTO tag_list_demo VALUES
(1, 'sql,php'), (2, 'php,css'), (3, 'nosql'), (4, ''), (5, NULL);
SELECT id, tags, FIND_IN_SET('sql', tags) AS sql_position
FROM tag_list_demo ORDER BY id;
SELECT id, tags FROM tag_list_demo
WHERE FIND_IN_SET('sql', tags) > 0 ORDER BY id;
SELECT id FROM tag_list_demo
WHERE FIND_IN_SET('sql', tags) = 0 ORDER BY id;
첫 조회의 위치는 id 순서대로 1·0·0·0·NULL입니다. 포함 조회는 id=1만 반환하고 nosql인 id=3은 제외됩니다. 미포함 조건은 id=2·3·4를 반환하며, NULL인 id=5는 비교 결과가 참이 아니므로 포함하지 않습니다. 누락 값도 미포함으로 취급한다면 IS NULL을 추가하는 등 원하는 규칙을 별도로 표현합니다. LIKE의 부분 일치와 항목 일치를 혼동하지 않는 것이 핵심입니다.
새 데이터 모델에서는 검색·수정이 자주 필요한 태그를 연결 테이블의 행으로 분리하는 방안을 우선 검토합니다. 쉼표 문자열은 항목별 유일성·외래 키·쉼표 포함 값을 다루기 어렵고, 일반 VARCHAR 열의 FIND_IN_SET 검색을 단순 인덱스 동등 비교와 같은 방식으로 기대할 수 없습니다. 실제 비용은 데이터 크기와 실행 계획으로 판단합니다.
SET 타입의 순번은 정의 목록 기준
MariaDB의 SET 타입은 허용할 항목 목록에서 여러 값을 선택해 저장하는 타입입니다. 일반 VARCHAR의 쉼표 목록과 내부 표현이 다르며, SET 타입 문서에서 저장 순서와 제한을 확인할 수 있습니다. 두 번째 인수가 SET 열이면 FIND_IN_SET은 비트 연산으로 최적화됩니다.
USE learning;
CREATE TABLE set_color_demo (
id INT PRIMARY KEY,
colors SET('red','green','blue') NULL
);
INSERT INTO set_color_demo VALUES (1, 'blue,red'), (2, NULL);
SELECT id, colors,
FIND_IN_SET('blue', colors) AS set_position,
FIND_IN_SET('green', colors) AS absent_position,
FIND_IN_SET('blue', CAST(colors AS CHAR)) AS text_position
FROM set_color_demo ORDER BY id;
첫 행의 colors 표시는 red,blue입니다. SET 열을 직접 사용한 blue 위치는 정의 목록의 세 번째 항목이므로 3이고, 선택되지 않은 green은 0입니다. CHAR로 변환한 일반 문자열에서는 blue가 표시 문자열의 두 번째 항목이므로 2입니다. 둘째 행의 위치는 모두 NULL입니다. 화면에 쉼표 목록처럼 보인다고 두 타입의 순번 의미까지 같다고 가정하지 마세요. SET 타입에도 항목 개수와 스키마 변경의 제약이 있으므로 정규화된 연결 테이블을 언제나 대체하는 것은 아닙니다.




