MariaDB 강좌 / NULL을 올바르게 비교하기: IS NULL·COALESCE
NULL은 값을 모르거나 값이 없다는 상태이므로 = NULL이나 <> NULL로 비교하지 않고 IS NULL·IS NOT NULL로 확인합니다. COALESCE는 첫 번째 NULL이 아닌 값을 조회 결과로 선택하며, 원본의 NULL을 저장된 기본값으로 바꾸는 명령은 아닙니다.
NULL·빈 문자열·0은 서로 다른 상태로 다루기
상품 메모를 아직 수집하지 않았다면 NULL, 사용자가 내용을 지우고 제출했다면 빈 문자열, 재고가 없다면 숫자 0으로 표현할 수 있습니다. 이 차이는 데이터 정책에 따라 정해야 합니다. 눈에 비어 보인다는 이유만으로 서로 같은 상태라고 처리하면 나중에 미수집 데이터의 수를 알 수 없게 됩니다. 입력 시 NULL 허용 여부는 NULL·기본값·NOT NULL·CHECK로 입력 조건 정하기에서 정합니다. 다른 조회의 결과로 제외 대상을 만들 때는 서브쿼리·EXISTS·NOT EXISTS로 조건 표현하기의 NOT EXISTS와도 비교해 보세요.
| 값 | SQL 표기 | 의미 |
|---|---|---|
| NULL | NULL | 알려지지 않거나 존재하지 않는 값 |
| 빈 문자열 | '' | 길이가 0인 문자열 |
| 숫자 0 | 0 | 알려진 수량이 0 |
| NULL이라는 글자 | 'NULL' | 네 글자로 된 일반 문자열 |
이 글은 EMPTY_STRING_IS_NULL 같은 호환 모드를 켜지 않은 일반적인 모드를 전제로 합니다. SELECT @@SESSION.sql_mode;로 현재 연결의 설정을 확인하세요. SQL 모드에 따른 차이를 숨기고 모든 환경에서 빈 문자열이 별도 값이라고 단정해서는 안 됩니다.
값이 없는 메모와 빈 메모를 함께 준비하기
명령줄 클라이언트에서 course_user로 학습용 서버에 접속하고 shopcourse 데이터베이스를 준비합니다. 접속과 UTF-8 SQL 파일 실행 방법은 명령줄 클라이언트와 SQL 파일 실행 안내를 참고하세요. 아래 코드는 운영 서버가 아닌 연습 환경에서 실행합니다.
이 글은 products_16_lab이라는 독립된 실습 테이블을 사용합니다. 이전 강좌의 products 등 기존 테이블에는 손대지 않습니다. SELECT VERSION(), DATABASE();와 SHOW TABLES LIKE 'products_16_lab';로 서버·선택한 데이터베이스·이름 중복을 먼저 확인합니다. 같은 이름이 이미 있으면 정의와 데이터를 확인하고, 이 글에서 만든 연습 데이터인지 판단한 뒤 마지막 절의 반복 방법을 따르세요.
USE shopcourse; CREATE TABLE products_16_lab ( product_id INT NOT NULL AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, category VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, note VARCHAR(100) NULL DEFAULT NULL, PRIMARY KEY (product_id), CONSTRAINT ck_p16_price CHECK (price >= 0), CONSTRAINT ck_p16_stock CHECK (stock >= 0) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO products_16_lab (product_id, product_name, category, price, stock, note) VALUES (1, '노트', '문구', 3500.00, 20, NULL), (2, '볼펜', '문구', 1500.00, 0, ''), (3, '머그컵', '생활', 8000.00, 12, '선물 포장'), (4, '스탠드', '생활', 25000.00, 5, NULL), (5, '파일', '문구', 1500.00, 50, ''), (6, '케이블', '전자', 8000.00, 0, '호환 확인');
SELECT product_id, note, note IS NULL AS is_missing,
note = '' AS is_empty
FROM products_16_lab ORDER BY product_id;
| 번호 | 메모 | is_missing | is_empty |
|---|---|---|---|
| 1 | NULL | 1 | NULL |
| 2 | 빈 문자열 | 0 | 1 |
| 3 | 선물 포장 | 0 | 0 |
| 4 | NULL | 1 | NULL |
| 5 | 빈 문자열 | 0 | 1 |
| 6 | 호환 확인 | 0 | 0 |
NULL 메모와 빈 문자열을 비교한 결과는 0이 아니라 NULL입니다. 이 결과를 false와 똑같은 값이라고 생각하면 NOT 같은 연산에서도 혼동하게 됩니다. SQL 조건에서는 참·거짓 외에 알 수 없음이 존재한다고 이해하면 도움이 됩니다.
IS NULL과 IS NOT NULL로 실제 행 선택하기
SELECT product_id FROM products_16_lab WHERE note = NULL ORDER BY product_id; SELECT product_id FROM products_16_lab WHERE note IS NULL ORDER BY product_id; SELECT product_id FROM products_16_lab WHERE note IS NOT NULL ORDER BY product_id;
첫 조회는 메모가 NULL인 행이 있어도 결과가 없습니다. note = NULL의 결과가 참이 아니기 때문입니다. IS NULL은 1·4번, IS NOT NULL은 2·3·5·6번을 고릅니다. IS NOT NULL에는 빈 문자열도 포함되므로 글자가 있는 메모만 고른다는 의미가 아닙니다.
SELECT product_id FROM products_16_lab WHERE note <> '선물 포장' ORDER BY product_id; SELECT product_id FROM products_16_lab WHERE note IS NULL OR note <> '선물 포장' ORDER BY product_id;
선물 포장과 다른 메모를 찾는 첫 조회는 2·5·6번입니다. 메모가 NULL인 1·4번은 비교 결과를 알 수 없어 빠집니다. 미입력까지 포함한 나머지를 원한다면 둘째 조회처럼 IS NULL을 OR로 연결해야 하며 결과는 1·2·4·5·6번입니다. MariaDB NULL 값의 비교 규칙은 일반 비교와 NULL-safe 비교의 차이를 설명합니다.
COALESCE로 표시값을 정하되 원본은 보존하기
SELECT product_id, COALESCE(note, '미입력') AS display_note
FROM products_16_lab ORDER BY product_id;
SELECT COALESCE(NULL, NULL, '기본 안내') AS fallback_text;
SELECT NULL = NULL AS ordinary_equal,
NULL <=> NULL AS null_safe_equal;
display_note는 1·4번에서 미입력, 2·5번에서 빈 문자열, 3번에서 선물 포장, 6번에서 호환 확인입니다. COALESCE는 빈 문자열을 건너뛰지 않습니다. 후보가 여러 개이면 왼쪽부터 첫 번째 NULL이 아닌 값을 선택하고, 모든 후보가 NULL이면 결과도 NULL입니다.
두 번째 SELECT의 fallback_text는 기본 안내이고, 세 번째 SELECT는 ordinary_equal=NULL, null_safe_equal=1입니다. <=>는 양쪽이 모두 NULL일 때도 동등 여부를 1 또는 0으로 판단하는 MariaDB의 NULL-safe 동등 연산자입니다. 단순히 한 열의 NULL을 찾을 때는 IS NULL이 의도를 더 쉽게 드러냅니다.
COALESCE의 후보는 가급적 같은 용도의 타입으로 맞춥니다. 숫자 재고의 대체값과 화면 문구를 한 식에 섞어 놓으면 후속 계산이나 정렬에서 혼동할 수 있습니다. COALESCE 공식 문서에서 반환값과 타입 관련 예외를 확인하세요. SELECT 뒤에 원본 메모를 다시 조회하면 여전히 NULL이며, 데이터 수정과 표시값 선택은 별개의 작업입니다.
NOT IN에 NULL이 섞이는 함정 확인하기
SELECT product_id FROM products_16_lab
WHERE category NOT IN ('문구', NULL) ORDER BY product_id;
문구가 아닌 생활·전자를 기대했더라도 이 예제는 결과가 없습니다. 문구인 행은 목록과 일치해 제외되고, 다른 분류는 NULL과 비교하는 과정에서 알 수 없는 결과가 남아 WHERE를 통과하지 못합니다. 제외 목록에서 NULL을 제거하면 category NOT IN ('문구')는 3·4·6번을 선택합니다.
목록을 애플리케이션이나 다른 조회에서 얻을 때는 NULL이 섞일 수 있는지 먼저 확인합니다. 여러 테이블에서 존재 여부를 판정하는 경우에는 NOT EXISTS가 적절할 수도 있지만, 두 문장을 맥락 없이 바꾸면 같은 결과가 된다고 가정하지 않습니다. 비교 대상 열 자체의 NULL을 포함할지도 별도로 정해야 합니다.
NULL 처리 정책을 확인하고 반복하기
COALESCE로 화면을 보기 좋게 만들었다고 데이터가 완전해진 것은 아닙니다. 미확인 재고를 0으로 보여 주면 품절로 오해할 수 있으므로 미확인이라는 문구나 상태 열이 더 적합한지 판단합니다. 실제 기본값이 필요하면 입력 정책과 DEFAULT를, 필수값이면 NOT NULL을 검토합니다. 기본값과 제약 조건 안내가 그 판단을 보완합니다.
클라이언트가 NULL을 빈칸으로 표시한다면 결과에 IS NULL 열을 함께 붙여 확인하세요. 예제의 핵심은 표시 모양보다 저장 상태와 조건의 참·거짓·알 수 없음이므로, 화면에서 보이지 않는 값을 임의로 지우거나 채워 넣지 않습니다.
다시 실습하려면 SELECT DATABASE();, SHOW CREATE TABLE products_16_lab;와 저장된 행을 확인합니다. 보관할 데이터가 없는 이 글의 연습 테이블이 확실할 때만 아래 문장으로 지운 뒤 준비 코드를 다시 실행합니다. DROP TABLE은 구조와 데이터를 함께 없애며 일반적인 ROLLBACK으로 취소되지 않습니다.
DROP TABLE shopcourse.products_16_lab;










