상품 목록에서 어떤 상품들이 분류 필터에 안 걸렸다. 확인해 보니 유형 컬럼이 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 인 이유를 나눠 봤다. 컬럼 도입 이전 자료가 만 삼천 건 남짓이고 도입 이후인데 입력 안 한 것과 일부러 비워 둔 것이 있는지가 남았다.
SELECT COUNT(*) FROM product
WHERE type IS NULL AND reg_date >= '2015-08-21';
-- 47
수십 건 있었다. 관리자 화면에서 그 항목이 필수가 아니어서 안 채운 것이다. 일부러 비우는 경우는 없다는 것도 확인했다.
NULL 이라는 사실은 같아도 이유가 다르다. 셋을 같은 값으로 채우면 서로 다른 것이 하나로 뭉개진다.
각각 다르게 다뤘다
도입 이전 자료 만 삼천 건 남짓은 값을 추정해서 채울지 별도 부류로 둘지 정해야 했다. 상품명과 분류로 추정할 수 있었지만 정확하지 않아서 unknown 이라는 값을 만들어 넣었다.
UPDATE product SET type = 'unknown'
WHERE type IS NULL AND reg_date < '2015-08-21';
이러면 필터에 미분류로 나온다. 안 걸리는 것보다 낫고 그것이 무엇인지도 알 수 있다.
도입 이후 수십 건은 채워야 하는 것이라 목록을 뽑아 담당자에게 넘겼다. 그리고 앞으로는 입력을 필수로 바꿔서 화면에서 안 넣으면 저장이 안 되게 했다.
제약을 바로 걸지 않은 이유
컬럼에 NOT NULL 을 걸려다 멈췄다. 아직 수십 건이 비어 있어서 걸린다.
순서를 이렇게 했다.
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을 붙일지 본다- 컬럼을 추가할 때 기존 행의 값을 무엇으로 할지 먼저 정한다