MariaDB / 함수 / LTRIM, RTRIM, TRIM / 양끝 문자열 제거 함수

MariaDB에서 왼쪽 공백은 LTRIM(), 오른쪽 공백은 RTRIM(), 양끝 공백은 TRIM()으로 제거합니다. TRIM은 공백 대신 제거할 문자열과 방향을 지정할 수도 있으며, 문자열 가운데에 있는 내용은 그대로 둡니다. 기본 공백 제거를 탭·줄바꿈·모든 유니코드 공백을 지우는 기능으로 생각하면 안 됩니다.

왼쪽·오른쪽·양끝 공백 비교

LTRIM()과 RTRIM()은 제거할 방향이 정해져 있습니다. 아래는 테이블 없이 MariaDB 접속에서 바로 실행할 수 있습니다. 결과를 QUOTE()로 감싸 끝에 남은 공백을 식별하기 쉽게 했습니다.

SELECT QUOTE(LTRIM('  12  34  ')) AS left_only,
       QUOTE(RTRIM('  12  34  ')) AS right_only,
       QUOTE(TRIM('  12  34  ')) AS both_ends;
left_only: '12  34  '
right_only: '  12  34'
both_ends: '12  34'

위 결과는 왼쪽·오른쪽·양끝 제거 뒤 남은 공백을 그대로 표시한 것입니다. 12와 34 사이의 공백 두 개는 모두 남아 있습니다. 문자열 안의 공백까지 없애거나 연속 공백을 하나로 줄이는 것은 별도의 변환 규칙이며 이 함수들의 기본 목적과 다릅니다. 표시 화면에서 끝 공백이 안 보인다면 QUOTE·HEX·CHAR_LENGTH 같은 표현으로 실제 값을 확인할 수 있습니다.

TRIM에 방향과 제거 문자열 지정

TRIM() 문서에서 BOTH는 양끝, LEADING은 왼쪽, TRAILING은 오른쪽을 뜻합니다. 방향을 생략하면 BOTH, 제거 문자열을 생략하면 공백이 기본입니다.

SELECT TRIM('*' FROM '**12**34**') AS both_stars,
       TRIM(LEADING '*' FROM '**12**34**') AS leading_stars,
       TRIM(TRAILING '*' FROM '**12**34**') AS trailing_stars,
       TRIM(BOTH 'ab' FROM 'ababXabab') AS repeated_chunk,
       TRIM(BOTH 'ab' FROM 'baXba') AS different_order;

결과는 12**34, 12**34**, **12**34, X, baXba입니다. 양끝의 일치하는 부분이 반복되면 계속 제거하지만 가운데의 **는 남습니다. 제거 문자열 ab는 a 또는 b라는 문자 집합이 아니라 ab라는 순서의 문자열입니다. 따라서 ba로 된 끝부분까지 같은 규칙으로 지우지는 않습니다. 여러 문자를 한 번에 넘기면 각 문자를 따로 지워 준다고 해석하지 마세요.

탭·줄바꿈이 남을 때 실제 바이트 확인

다음은 탭을 CHAR(9), 줄바꿈을 CHAR(10)으로 만들어 기본 TRIM 동작과 비교합니다. 눈으로 공백처럼 보이는 문자라도 종류가 다를 수 있습니다.

SELECT HEX(TRIM(' abc ')) AS ordinary_spaces,
       HEX(TRIM(CONCAT(CHAR(9), ' abc ', CHAR(10)))) AS tab_and_lf,
       HEX(TRIM(CHAR(9) FROM CONCAT(CHAR(9), 'abc', CHAR(9)))) AS explicit_tab;

ordinary_spaces는 616263으로 abc의 바이트만 남습니다. tab_and_lf는 0920616263200A여서 앞 탭·공백과 뒤 공백·줄바꿈이 유지됩니다. 첫 문자·끝 문자가 기본 제거 대상 공백이 아니므로 그 안쪽의 공백까지 건너뛰어 지우지 않습니다. explicit_tab은 제거 대상을 탭으로 지정했으므로 616263입니다.

CSV·복사한 텍스트에서 값이 계속 다르게 보이면 문자 인코딩과 HEX 결과를 먼저 확인합니다. 모든 보이지 않는 문자를 일괄 삭제하기보다 허용할 문자와 제거할 위치를 정하세요. 줄바꿈이 의미 있는 메모나 공백을 보존해야 하는 코드에는 이름 정리와 같은 규칙을 그대로 적용하지 않습니다.

열 값 정리와 빈 문자열·NULL 구분

다음은 실습용 learning 데이터베이스에서 CREATE·INSERT·SELECT 권한으로 실행합니다. trim_demo가 이미 있다면 다른 실습 이름으로 바꾸세요. 원본을 바로 수정하지 않고 조회에서 정리 전후를 확인합니다.

USE learning;
CREATE TABLE trim_demo (
    id INT PRIMARY KEY,
    label VARCHAR(100) NULL
) DEFAULT CHARSET=utf8mb4;
INSERT INTO trim_demo VALUES
    (1, '  연필  '), (2, '    '), (3, NULL), (4, '메모  소형');
SELECT id, QUOTE(label) AS original_value,
       QUOTE(TRIM(label)) AS trimmed_value,
       NULLIF(TRIM(label), '') AS empty_to_null
FROM trim_demo ORDER BY id;

id=1의 trimmed_value는 '연필', id=2는 빈 문자열을 나타내는 '', id=3은 NULL입니다. id=4의 가운데 공백 두 개는 남습니다. empty_to_null은 정리 뒤 빈 문자열을 NULL로 바꿔 id=2·3 모두 NULL이 됩니다. 이것은 “공백뿐인 입력은 미입력으로 취급한다”는 규칙을 별도로 적용한 것이며, TRIM 자체가 항상 빈 문자열을 NULL로 바꾸는 것은 아닙니다.

위 결과는 일반 SQL 모드 기준입니다. 공식 문서처럼 Oracle 호환 모드에서는 빈 결과가 NULL이 되는 차이가 있고 TRIM_ORACLE·LTRIM_ORACLE·RTRIM_ORACLE로 해당 동작을 선택할 수도 있습니다. 기존 시스템과 결과가 다르면 현재 @@session.sql_mode와 실제 호출 함수를 확인합니다. NULL 입력은 보통 이 함수들의 NULL 결과로 이어지므로 빈 문자열과 구분해서 처리하세요.

실제 저장값을 바꾸기 전에 확인할 점

SELECT에서 TRIM을 사용한 것은 표시·조회 결과의 변환이며 저장된 label은 그대로입니다. 실제 수정이 필요하면 먼저 대상 행과 정리 결과를 비교하고, 정리 뒤 중복되는 값·필수 값 누락·길이 제한·연결된 참조에 영향이 있는지 확인해야 합니다. “표시가 깔끔해졌다”는 이유만으로 모든 문자열 열의 공백을 일괄 제거하지 않습니다.

수정 작업은 WHERE로 범위를 정하고 백업·트랜잭션 등 해당 데이터의 복구 방법을 준비해 진행합니다. 필요가 단순 표시라면 조회에서만 변환하는 방법도 있습니다. 값이 계속 다르게 보일 때는 함수 방향, 제거 문자열의 순서, 가운데 문자와 양끝의 차이, SQL 모드, HEX 결과를 차례로 확인하면 원인을 좁힐 수 있습니다.