Skip to content
isdnetworks
Go back

조건 하나가 전부를 걸렀다

통계 화면이 비어 있었다. 어제까지는 나왔고 쿼리를 그대로 돌려도 0건이다.

Table of contents

Open Table of contents

증상 — 조건을 하나씩 뺐다

돌린 쿼리는 이랬다.

SELECT COUNT(*) FROM order_item oi
JOIN orders o ON o.order_no = oi.order_no
JOIN product p ON p.product_no = oi.product_no
WHERE o.status = 'done'
  AND o.pay_date >= '2014-09-01'
  AND p.category_no IN (12, 15)
  AND oi.cancel_yn = 'N';
-- 0

조건이 넷이라 하나씩 빼 봤다.

-- cancel_yn 빼기 → 0
-- category_no 빼기 → 0
-- pay_date 빼기 → 0
-- status 빼기 → 4,102

status = 'done' 이 전부를 걸렀다.

0건이라는 결과만으로는 어느 조건 때문인지 알 수 없다. 하나씩 빼는 것은 단순하지만 확실한 방법이고 조건이 넷이면 많아야 네 번만 돌려 보면 나온다.

값이 바뀌어 있었다

상태 값을 확인했다.

SELECT status, COUNT(*) FROM orders
WHERE pay_date >= '2014-09-01' GROUP BY status;
status      count
completed    4102

done 이 아니라 completed 였다. 전날 다른 사람이 상태 값 체계를 바꾸면서 이 쿼리를 안 고친 것이었다.

바꾼 쪽은 자기가 쓰는 화면만 확인하고 넘어간 상태였다. 그 값을 우리 쪽에서도 읽고 있다는 것을 몰랐던 것이다.

조인도 확인했다

이번에는 조건이 원인이었지만 조건을 다 빼도 0이 나오는 경우가 있다. 그때는 조인을 본다.

SELECT COUNT(*) FROM order_item;                     -- 12,041
SELECT COUNT(*) FROM order_item oi JOIN orders o ON o.order_no = oi.order_no;  -- 12,041
SELECT COUNT(*) FROM order_item oi JOIN product p ON p.product_no = oi.product_no;  -- 11,890

세 번째에서 줄었다. 상품이 지워진 주문 항목이 151건이었다. 통계 목적에 따라 JOIN 이 아니라 LEFT JOIN 이 맞을 수도 있다.

여기에 한 번 걸린 적이 있다. LEFT JOIN 으로 바꿨는데도 여전히 0건이 나왔다. 원인은 그 표의 컬럼 조건이 WHERE 에 남아 있었기 때문이다.

MySQL 은 WHERE 의 조건이 null-rejected 이면 LEFT JOININNER JOIN 으로 바꿔 실행한다. null-rejected 란 NULL 로 채워진 행에 대해 FALSEUNKNOWN 이 되는 조건을 말한다. 안 붙은 행은 그 컬럼이 NULL 이라 조건이 거짓이 되고 그러면 LEFT JOIN 을 쓴 의미가 없어진다. 그 조건은 WHERE 가 아니라 ON 절로 옮겨야 했다.

0건이 나올 때 볼 순서를 정했다.

1. 조건을 하나씩 빼서 어느 조건인지 찾는다
2. 그 조건의 실제 값 분포를 본다 (GROUP BY)
3. 조건을 다 빼도 0이면 조인을 하나씩 확인한다
4. 테이블 자체가 비었는지 본다 (COUNT(*))

4번을 마지막에 둔 것은 대개 그건 아니어서다. 다만 확인은 한다.

값 체계를 바꿀 때의 절차

상태 값을 바꾼 쪽에서 쓰는 곳을 다 찾았어야 했다.

$ grep -rn "'done'" --include=*.php application/ | wc -l
      14

14군데였고 그중 6곳이 안 고쳐져 있었다. 그래서 값을 상수로 뺐다.

class OrderStatus {
    const COMPLETED = 'completed';
}

문자열을 직접 쓰면 검색으로 찾아야 하고 빠뜨린다. 상수면 최소한 한 곳에 모인다.

값 체계를 바꿀 때의 절차도 적었다.

1. 쓰는 곳을 전부 찾는다 (코드·쿼리·배치·보고서)
2. 두 값을 다 받아들이는 기간을 둔다
3. 자료를 바꾼다
4. 코드를 새 값만 쓰게 바꾼다
5. 옛 값이 남았는지 확인한다

2번을 안 하면 바꾸는 순간 뭔가 깨진다.

// 이행 기간
if (in_array($order->status, ['done', 'completed'], true)) { ... }

통계 화면이 조용히 비어 있었던 것도 손봤다. 그게 정상인지 아닌지 화면만 봐서는 모른다.

if (empty($rows)) {
    log_message('warning', "통계 조회 0건: {$sql}");
}

0건이 나오는 조회는 그 자체를 기록한다. 정상일 수도 있지만 이상일 때 단서가 된다.

정리


Share this post on:

Previous Post
옵션 조합이 폭발했다
Next Post
판을 올렸더니 쓰던 기능이 없어졌다