SELECT ENAME, SAL, COMM, SAL + COMM AS TOTAL, SAL + NVL(COMM, 0) AS TOTAL2 FROM EMP WHERE DEPTNO = 30 ORDER BY ENAME;
ENAME SAL COMM TOTAL TOTAL2---------- ---------- ---------- ---------- ----------ALLEN 1600 300 1900 1900BLAKE 2850 2850JAMES 950 950MARTIN 1250 1400 2650 2650TURNER 1500 0 1500 1500WARD 1250 500 1750 1750
BLAKE·JAMES 는 COMM 이 NULL 이라 TOTAL 도 NULL 이다. 급여 2850 이 있어도 결과는 빈칸이다.
TURNER 의 COMM 은 0 이다. 0 은 값이므로 1500 + 0 = 1500 이 정상으로 나온다. NULL 과 0 을 같은 것으로 보면 틀린다.
비교 쪽은 오류 없이 행만 사라진다.
SELECT (SELECT COUNT(*) FROM EMP WHERE COMM = NULL) AS EQ_NULL, (SELECT COUNT(*) FROM EMP WHERE COMM IS NULL) AS IS_NULL, (SELECT COUNT(*) FROM EMP WHERE COMM <> 0) AS NE_ZERO FROM DUAL;
SELECT COUNT(*) AS CNT_ALL, COUNT(COMM) AS CNT_COMM, COUNT(MGR) AS CNT_MGR, COUNT(DISTINCT MGR) AS CNT_DMGR, COUNT(DISTINCT DEPTNO) AS CNT_DEPT FROM EMP;
GROUP BY 없는 집계는 대상 행이 없어도 결과 1행을 만든다. COUNT 는 0, SUM·MAX 는 NULL 이다.
0 이 필요하면 NVL(SUM(SAL), 0) 처럼 집계 바깥을 감싼다. SUM(NVL(SAL, 0)) 은 더할 행이 없으니 여전히 NULL 이다.
같은 조건에 GROUP BY DEPTNO 를 붙이면 만들 그룹이 없어 결과가 0행이다. COUNT 0 인 행이 나오지 않는다.
⚠️ 함정: "40번 부서의 사원 수"를 SELECT DEPTNO, COUNT(*) FROM EMP WHERE DEPTNO = 40 GROUP BY DEPTNO 로 구하면 0 이 아니라 0행이다. 사원 없는 부서까지 0 으로 보려면 DEPT 기준 외부 조인 뒤 COUNT(E.EMPNO) 처럼 NULL 이 되는 열을 센다.
IN 은 OR, NOT IN 은 AND 로 풀린다(W5 Day 1). 목록에 NULL 이 있을 때 둘의 결과가 갈린다.
SELECT (SELECT COUNT(*) FROM EMP WHERE DEPTNO IN (10, NULL)) AS IN_CNT, (SELECT COUNT(*) FROM EMP WHERE DEPTNO NOT IN (10, NULL)) AS NOT_IN_CNT FROM DUAL;
IN_CNT NOT_IN_CNT---------- ---------- 3 0
DEPTNO IN (10, NULL) 은 DEPTNO = 10 OR DEPTNO = NULL 이다. 앞쪽이 참인 10번 부서 3명은 남는다.
DEPTNO NOT IN (10, NULL) 은 DEPTNO <> 10 AND DEPTNO <> NULL 이다. 뒤쪽이 늘 UNKNOWN 이라 AND 전체가 참이 될 수 없다.
서브쿼리가 NULL 을 돌려줄 때도 같다. "급여가 누구의 수당 액수와도 같지 않은 사원"을 두 가지로 구한다.
SELECT COUNT(*) FROM EMP WHERE SAL NOT IN (SELECT COMM FROM EMP);
COUNT(*)---------- 0
SELECT COUNT(*) FROM EMP E WHERE NOT EXISTS (SELECT 1 FROM EMP C WHERE C.COMM = E.SAL);
COUNT(*)---------- 14
수당 값은 300·500·1400·0 이고 이와 같은 급여는 없다. 정답은 14명인데 NOT IN 은 0 을 냈다. 서브쿼리 결과 14행 중 10행이 NULL 이기 때문이다.
NOT EXISTS 는 "조건을 만족하는 행이 하나도 없는가"만 본다. C.COMM = E.SAL 이 UNKNOWN 인 행은 "만족하는 행"이 아닐 뿐이라 결과를 망치지 않는다.
NOT IN 을 살리려면 서브쿼리에 WHERE COMM IS NOT NULL 을 넣는다. 그러면 14가 나온다.
SELECT EMPNO, ENAME, COMM, MGR, NVL(COMM, 0) AS NV, NVL2(COMM, 'Y', 'N') AS NV2, NULLIF(COMM, 0) AS NIF, COALESCE(COMM, MGR, 0) AS COAL FROM EMP WHERE EMPNO IN (7369, 7499, 7839, 7844) ORDER BY EMPNO;
EMPNO ENAME COMM MGR NV N NIF COAL---------- ---------- ---------- ---------- ---------- - ---------- ---------- 7369 SMITH 7902 0 N 7902 7499 ALLEN 300 7698 300 Y 300 300 7839 KING 0 N 0 7844 TURNER 0 7698 0 Y 0
함수
돌려주는 값
헷갈리는 지점
NVL(a, b)
a 가 NULL 이면 b, 아니면 a
인자는 둘뿐이다
NVL2(a, b, c)
a 가 NULL 이 아니면 b, NULL 이면 c
b·c 순서를 뒤집기 쉽다. TURNER 의 0 은 Y 다
NULLIF(a, b)
a 와 b 가 같으면 NULL, 다르면 a
NULL 을 없애는 함수가 아니라 만드는 함수다
COALESCE(a, b, …)
왼쪽부터 처음 만난 NULL 아닌 값
SMITH 는 COMM 이 NULL 이라 MGR 7902 가 나온다
NV2 열 머리가 N 한 글자로 잘린 것은 SQL*Plus 가 값의 폭(1자)에 맞춘 탓이다.
TURNER 줄에서 NULLIF 는 0 을 NULL 로 바꾸고, NVL 은 그대로 0 을 둔다. 두 함수는 방향이 반대다.
SELECT ENAME, COMM, CASE WHEN COMM = NULL THEN 'NONE' ELSE 'HAS' END AS EQ, CASE COMM WHEN NULL THEN 'NONE' ELSE 'HAS' END AS SMP, CASE WHEN COMM IS NULL THEN 'NONE' ELSE 'HAS' END AS ISN, DECODE(COMM, NULL, 'NONE', 'HAS') AS DEC FROM EMP WHERE DEPTNO = 30 ORDER BY ENAME;
ENAME COMM EQ SMP ISN DEC---------- ---------- ---- ---- ---- ----ALLEN 300 HAS HAS HAS HASBLAKE HAS HAS NONE NONEJAMES HAS HAS NONE NONEMARTIN 1400 HAS HAS HAS HASTURNER 0 HAS HAS HAS HASWARD 500 HAS HAS HAS HAS
EQ 열: WHEN COMM = NULL 은 UNKNOWN 이라 WHEN 이 성립하지 않고 모두 ELSE 로 간다.
SMP 열: 단순 CASE CASE COMM WHEN NULL 도 속으로는 COMM = NULL 비교라 결과가 같다.
ISN 열: NULL 을 가르려면 검색 CASE 에 IS NULL 을 쓴다.
DEC 열: DECODE 는 NULL 과 NULL 을 같다고 본다. 같은 뜻으로 보이는 CASE 와 DECODE 가 여기서 갈린다.
⚠️ 함정: ELSE 를 빼고 CASE WHEN COMM = NULL THEN 1 END 를 COUNT 하면 오류 없이 0 이다. 같은 자리에 DECODE(COMM, NULL, 1) 를 쓰면 NULL 인 행 수가 나온다.