상품 목록에서 어떤 상품들이 분류 필터에 안 걸렸다. 확인해 보니 유형 컬럼이 NULL 이었는데 값을 채우려다 그 상품들의 공통점을 먼저 봤다.
Table of contents
Open Table of contents
등록 시점이 몰려 있었다
SELECT MIN(reg_date), MAX(reg_date), COUNT(*)
FROM product WHERE type IS NULL;
2013-04-11 2015-08-20 13,042
2015년 8월 이후 등록분에는 없다. 컬럼이 그때 추가된 것이다.
SELECT MIN(reg_date) FROM product WHERE type IS NOT NULL;
-- 2015-08-21
ALTER TABLE ADD COLUMN 을 기본값 없이 하면 기존 행이 전부 NULL 이 된다. 컬럼을 추가한 날 이전 자료는 값이 없고 오류가 아니라 그때 그 개념이 없었던 것이다.
세 가지 경우가 섞여 있었다
NULL 인 이유를 나눠 봤다. 컬럼 도입 이전 자료가 13,042건이고 도입 이후인데 입력 안 한 것과 일부러 비워 둔 것이 있는지가 남았다.
SELECT COUNT(*) FROM product
WHERE type IS NULL AND reg_date >= '2015-08-21';
-- 47
47건 있었다. 관리자 화면에서 그 항목이 필수가 아니어서 안 채운 것이다. 일부러 비우는 경우는 없다는 것도 확인했다.
NULL 이라는 사실은 같아도 이유가 다르다. 셋을 같은 값으로 채우면 서로 다른 것이 하나로 뭉개진다.
각각 다르게 다뤘다
도입 이전 자료 13,042건은 값을 추정해서 채울지 별도 부류로 둘지 정해야 했다. 상품명과 분류로 추정할 수 있었지만 정확하지 않아서 unknown 이라는 값을 만들어 넣었다.
UPDATE product SET type = 'unknown'
WHERE type IS NULL AND reg_date < '2015-08-21';
이러면 필터에 미분류로 나온다. 안 걸리는 것보다 낫고 그것이 무엇인지도 알 수 있다.
도입 이후 47건은 채워야 하는 것이라 목록을 뽑아 담당자에게 넘겼다. 그리고 앞으로는 입력을 필수로 바꿔서 화면에서 안 넣으면 저장이 안 되게 했다.
제약을 바로 걸지 않은 이유
컬럼에 NOT NULL 을 걸려다 멈췄다. 아직 47건이 비어 있어서 걸린다.
순서를 이렇게 했다.
1. 이전 자료를 unknown 으로 채운다
2. 47건을 담당자가 채운다
3. 화면에서 필수로 만든다
4. 한 달 뒤에 NOT NULL 제약을 건다
4번을 바로 안 한 것은 다른 경로로 들어오는 것이 있을 수 있어서다. 입력 화면 말고 배치나 연동으로 INSERT 되는 경로가 있는지 확인이 안 됐다.
SELECT COUNT(*) FROM product WHERE type IS NULL AND reg_date >= '2016-03-26';
0이면 다른 경로가 없다는 뜻인데 실제로 5건이 나왔다. 일괄 등록 화면이 이 항목을 안 넣고 있었다. 제약을 바로 걸었으면 그 화면이 깨졌을 것이다.
검증 — NULL이 빠지는 조회와 절차
NULL 은 비교에서 참이 안 된다.
SELECT * FROM product WHERE type != 'gift'; -- NULL 행이 빠진다
선물이 아닌 상품을 뽑는 조회에서 NULL 행이 다 빠져 있었다. NULL 과의 비교 결과가 다시 NULL 이라 조건이 참이 안 되기 때문이다.
SELECT * FROM product WHERE type != 'gift' OR type IS NULL;
이런 조회가 몇 군데 있는지 찾았다.
$ grep -rn "type !=\|type <>" --include=*.php application/
여섯 곳이었다. unknown 으로 채우고 나서는 문제가 없어졌지만 다른 컬럼에도 같은 것이 있을 수 있어 적어 뒀다.
이 일을 겪고 나서 컬럼 추가 시 확인할 것도 적었다.
1. 기존 행의 값을 무엇으로 할지 정한다 (기본값? NULL? 일괄 갱신?)
2. NULL 을 허용한다면 그것이 무슨 뜻인지 적는다
3. 조회에서 NULL 이 빠지는 곳이 있는지 본다
4. 입력 경로가 여럿이면 전부 확인한다
5. 제약을 걸기 전에 실제로 NULL 이 안 생기는지 기간을 두고 본다
1번을 안 정하면 이번 같은 일이 난다.
정리
- 컬럼 도입 이전 자료는 값이 비어 있고 그것도 하나의 부류다
ADD COLUMN을 기본값 없이 하면 기존 행이 전부NULL이 된다NULL인 이유를 나눠 본다- 도입 이전인지 입력 안 한 것인지 일부러 비운 것인지 갈린다
- 이유가 다르면 다르게 다룬다
- 이전 자료는
unknown처럼 이름 붙은 값으로 채우면 필터에 나온다 NOT NULL을 바로 걸지 말고 배치·연동 경로를 기간을 두고 본다WHERE x != 'A'에서NULL행이 빠지니IS NULL을 붙일지 본다- 컬럼을 추가할 때 기존 행의 값을 무엇으로 할지 먼저 정한다