PostgreSQL 18의 CHECK (price > 0)은 식이 TRUE 또는 NULL이면 통과합니다. price가 NULL이면 비교 결과도 NULL이므로 CHECK만으로 NULL을 막지 못합니다.

NULL을 금지하려면 NOT NULL을 별도로 선언해야 합니다. 값의 범위와 값의 존재 여부는 서로 다른 규칙이므로 CHECK와 NOT NULL을 각각 확인합니다.

CHECK는 거짓을 막고 NULL은 별도로 다뤄야 합니다

PostgreSQL의 CHECK 제약은 행이 삽입되거나 갱신될 때 식을 평가합니다. 식이 참이면 조건을 만족합니다. 거짓이면 제약 위반입니다. PostgreSQL 18 문서는 식이 참 또는 NULL로 평가될 때 CHECK를 만족한다고 명시합니다. 따라서 CHECK (price > 0)을 price가 반드시 있어야 한다는 뜻으로 읽으면 안 됩니다. price에 숫자 12가 들어오면 12 > 0은 TRUE입니다. 0이나 -3은 FALSE라 저장을 거부합니다. 값이 NULL이면 NULL > 0은 참도 거짓도 아닌 NULL 결과입니다. 이 결과는 CHECK가 허용하므로 NULL 입력은 통과합니다. 이 점이 시험에서 묻는 핵심입니다. CHECK라는 이름을 보고 ‘조건식이 TRUE가 아니면 모두 거부한다’고 외우면 NULL 처리에서 오답을 냅니다.

CHECK 결과와 NULL 제약의 효과를 따로 계산합니다
price 입력price > 0CHECK만 적용CHECK와 NOT NULL 적용
12TRUE통과통과
0FALSE거부거부
-3FALSE거부거부
NULLNULL/UNKNOWN통과거부
PostgreSQL 18 문법을 따라 만든 교육용 테이블 정의입니다. 서버에서 실행한 예제가 아닙니다.
CREATE TABLE product_offer (
    sku text PRIMARY KEY,
    optional_price numeric CHECK (optional_price > 0),
    required_price numeric NOT NULL CHECK (required_price > 0)
);

같은 CHECK에 NOT NULL을 더하면 NULL 행만 달라집니다

위 테이블에서 optional_price는 양수 또는 NULL을 허용합니다. 0과 음수는 CHECK가 거부합니다. required_price는 CHECK식 자체는 같은 형태지만 NOT NULL도 함께 지정했습니다. 그러므로 NULL은 NOT NULL에서 거부되고, 0이나 음수는 CHECK에서 거부됩니다. 조건 두 개가 검사하는 대상이 다릅니다. 하나는 허용할 수치 범위를 검사하고, 다른 하나는 빠진 값이 없어야 한다고 선언합니다. 이 구분은 CHECK (amount IS NOT NULL AND amount > 0)처럼 하나의 긴 식을 쓰는 방법과도 관련이 있습니다. PostgreSQL에서는 CHECK 안에 NOT NULL을 적어도 동작은 만들 수 있지만, 명시적인 NOT NULL 제약은 의도를 더 분명히 표현합니다. 각 제약의 종류를 문제에서 묻는다면 입력값 하나가 어떤 규칙에서 탈락했는지도 따로 표시하세요.

입력값을 TRUE, FALSE, NULL 세 상태로 나눕니다

  1. CHECK 식에 들어가는 열 값이 NULL인지 먼저 표시합니다.
  2. NULL이 아니면 비교를 계산해 TRUE인지 FALSE인지 적습니다.
  3. TRUE는 CHECK를 통과시키고 FALSE는 제약 위반으로 봅니다.
  4. 식 결과가 NULL이면 PostgreSQL CHECK는 이를 통과시킵니다.
  5. NULL 입력 자체가 금지인지 확인하고 필요하면 NOT NULL을 별도로 적용합니다.

예를 들어 discounted_price > 0 AND list_price > discounted_price라는 CHECK를 생각해 보겠습니다. 두 열 중 하나라도 NULL이면 비교식 전체가 NULL일 수 있습니다. 그 경우에도 CHECK는 통과할 수 있습니다. 반대로 둘 다 숫자이고 첫 비교가 FALSE라면 전체 결과는 FALSE로 확정되어 거부됩니다. 복합식을 한꺼번에 외우지 말고 각 비교 결과를 만든 다음 AND의 3값 논리를 적용하면 어느 행이 통과하는지 보입니다. 다만 본문 예제에서는 산술 범위만 확인하므로 복잡한 논리식으로 확장하지 않겠습니다.

CHECK 통과를 데이터가 완전하다는 뜻으로 받아들이지 않습니다

흔한 실수는 제약이 통과했으니 그 열에 숫자가 저장되어 있다고 결론 내리는 것입니다. PostgreSQL 문서가 말하는 CHECK 통과는 검사식이 FALSE가 아니라는 뜻입니다. 결과가 NULL일 수도 있으므로 ‘가격이 양수다’와 ‘가격이 반드시 존재한다’는 별도 조건입니다. 또한 PostgreSQL 18의 설명을 다른 DBMS의 NULL 제약 동작과 무심코 동일시하지 마세요. 이번 글의 정답은 버전이 지정된 PostgreSQL 문서를 기준으로 합니다. SQL 예제는 문서의 규칙에 따라 논리값을 직접 계산했으며 PostgreSQL 서버나 원격 DB에서 실행하지 않았습니다. 실제 스키마에서 NULL을 허용해야 하는지는 업무 요구를 확인한 뒤 결정해야 합니다.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

PostgreSQL 18의 CHECK(score >= 0)에서 NULL, 0, -1은 각각 통과하나요? NULL을 막을 제약은 무엇인가요?

정답과 해설 확인NULL 비교는 NULL/UNKNOWN이므로 CHECK를 통과합니다. 0은 TRUE라 통과하고 -1은 FALSE라 거부됩니다. NULL도 금지하려면 NOT NULL을 추가합니다.

PostgreSQL 18 문서는 CHECK가 TRUE 또는 NULL 결과를 허용한다고 명시합니다. NOT NULL은 별도 제약입니다.