AD JB Toolbox 01

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;
같은 카테고리의 다른 글
MariaDB / 연산자 / 논리 연산자

MariaDB / 연산자 / 논리 연산자

MariaDB의 논리 연산자에 대하여 알아보겠습니다. 예제로 사용하는 테이블은 아래의 데이터를 가진 T_2이라는 테이블입니다. 각 열의 데이터 형식은 다음과 같습니다. C_1 : 정수 C_2 : 문자 +----+------+------+ | ID | C_1 | C_2 | +----+------+------+ | 1 | 1 | A | | 2 | ...

MariaDB 강좌 / 윈도 함수로 누적 합계와 이전·다음 행 비교하기

MariaDB 강좌 / 윈도 함수로 누적 합계와 이전·다음 행 비교하기

누적 합계와 LAG·LEAD를 계산하고 동점 데이터로 ROWS·RANGE를 비교합니다. 날짜 필터를 누적 전후에 적용한 결과와 정렬·기간 경계를 확인합니다.

MariaDB / 피벗 테이블 만드는 방법

MariaDB / 피벗 테이블 만드는 방법

피벗 테이블은 엑셀의 유용한 기능 중의 하나입니다. 예를 들어 아래의 왼쪽 데이터를 오른쪽 표처럼 만드는 것이 피벗 테이블입니다. 조건에 따라 그룹화시켜서 보기 좋게 데이터를 정리하는 것이라 보면 됩니다. MariaDB에서도 몇 가지 함수를 이용하여 피벗 테이블을 만들 수 있습니다.

MariaDB 강좌 / INNER JOIN으로 서로 연결된 데이터 조회하기

MariaDB 강좌 / INNER JOIN으로 서로 연결된 데이터 조회하기

고객과 주문을 INNER JOIN으로 연결하고 결과 한 행의 의미를 확인합니다. ON과 WHERE, 테이블 별칭, 일대다로 늘어나는 행과 조건 없는 곱집합을 비교합니다.

MariaDB / Query / LEFT JOIN, RIGHT JOIN, INNER JOIN

MariaDB / Query / LEFT JOIN, RIGHT JOIN, INNER JOIN

SQL의 JOIN은 여러 테이블을 한 번에 나타낼 때 사용합니다. 대표적인 JOIN 방식은 LEFT JOIN, RIGHT JOIN, INNER JOIN이 있습니다. 다음은 세 가지 JOIN의 차이를 알아보기 위한 간단한 예제 테이블입니다.

MariaDB 강좌 / UNION·UNION ALL로 조회 결과 합치기

MariaDB 강좌 / UNION·UNION ALL로 조회 결과 합치기

UNION과 UNION ALL을 겹치는 서울 고객·결제 고객 목록으로 비교합니다. 출처 열이 중복 기준을 바꾸는 이유, 열 위치·타입·전체 정렬의 조건을 설명합니다.

MariaDB 강좌 / 트랜잭션 시작하기: InnoDB·COMMIT·ROLLBACK

MariaDB 강좌 / 트랜잭션 시작하기: InnoDB·COMMIT·ROLLBACK

InnoDB에서 같은 변경을 ROLLBACK과 COMMIT으로 끝내어 미확정·확정의 차이를 비교합니다. 연결 유지, 영향 행 수 검사, 오류 처리와 구조 변경·외부 작업의 취소 한계를 설명합니다.

MariaDB 강좌 / 백업을 별도 데이터베이스에 복원하고 검증하기

MariaDB 강좌 / 백업을 별도 데이터베이스에 복원하고 검증하기

새 원본과 별도 빈 DB를 준비해 덤프를 복원하고 객체·행 수·키·업무 합계를 비교합니다. 파일 안의 DB 전환·삭제 문장, 클라이언트 호환성과 부분 복원 실패의 대응을 설명합니다.

MariaDB 강좌 / 명령줄 클라이언트로 접속하고 SQL 파일 실행하기

MariaDB 강좌 / 명령줄 클라이언트로 접속하고 SQL 파일 실행하기

터미널 명령과 SQL 입력 위치를 구분하고 학습 계정으로 접속합니다. UTF-8 SQL 파일의 SOURCE 실행, 예상 결과, 오류 중단·한글 표시·경로 문제를 함께 확인합니다.

MariaDB 강좌 / 문자열 타입 선택하기: CHAR·VARCHAR·TEXT

MariaDB 강좌 / 문자열 타입 선택하기: CHAR·VARCHAR·TEXT

CHAR·VARCHAR·TEXT를 저장 목적에 따라 선택하고 문자 수·바이트 수·끝 공백을 비교합니다. 한글과 이모지 입력, 길이 초과 오류, 정렬 규칙과 긴 본문 저장 시 주의점을 설명합니다.