데이터를 필터링하다 보면 조건은 맞는 것 같은데 결과가 줄어드는 순간이 있습니다.
특정 값을 제외했을 뿐인데, 원하는 데이터까지 같이 사라집니다.
이런 경우, NULL 때문에 조건이 의도와 다르게 동작할 수도 있습니다.
| order_id | order_status | cancel_reason |
|---|---|---|
| 1001 | COMPLETED | NULL |
| 1002 | COMPLETED | NULL |
| 1003 | CANCELED | OUT_OF_STOCK |
| 1004 | CANCELED | CUSTOMER_CHANGED_MIND |
| 1005 | CANCELED | PAYMENT_FAILED |
| 1006 | CANCELED | FRAUD_SUSPECTED |
| 1007 | COMPLETED | NULL |
품절(OUT_OF_STOCK)과 고객 변심(CUSTOMER_CHANGED_MIND) 취소만 제외하고 주문을 조회하려고 다음과 같이 작성합니다.
SELECT *
FROM orders
WHERE cancel_reason <> 'OUT_OF_STOCK'
AND cancel_reason <> 'CUSTOMER_CHANGED_MIND';
의도는 명확하지만, 이 쿼리는 cancel_reason이 NULL인 주문도 모두 제외합니다.
cancel_reason이 NULL이면 비교 결과는 다음과 같습니다.
NULL <> 'OUT_OF_STOCK' → UNKNOWN
NULL <> 'CUSTOMER_CHANGED_MIND' → UNKNOWN
UNKNOWN AND UNKNOWN → UNKNOWN
WHERE 절에서는 TRUE인 행만 반환되므로, UNKNOWN은 결과에서 제외됩니다.
SELECT *
FROM orders
WHERE (
cancel_reason <> 'OUT_OF_STOCK'
AND cancel_reason <> 'CUSTOMER_CHANGED_MIND'
)
OR cancel_reason IS NULL;
• 품절/변심 취소 제외
• 정상 주문(NULL) 포함
의도한 결과를 그대로 얻을 수 있습니다.
✔ NULL이 포함된 비교식은 TRUE/FALSE가 아닌 UNKNOWN이 될 수 있습니다.
✔ NULL을 포함해야 한다면 조건에서 명시적으로 처리해 보세요.