코딩 테스트 연습/[프로그래머스][리트코드] MySQL

1934. Confirmation Rate

duswjd_data 2025. 10. 13. 09:30

문제

https://leetcode.com/problems/confirmation-rate/description/


문제 설명

  • 각 사용자의 확인율(confirmation rate) 을 계산하라.
    • Signups : 모든 가입자 (user_id, 가입 시각)
    • Confirmations : 각 가입자가 보낸 “확인 요청” 기록
      • action 값은 'confirmed' 또는 'timeout'
  • 확인율 = confirmed 개수 / 전체 요청 개수
    • 확인 요청이 없는 사용자의 경우 → 확인율은 0.00
    • 결과는 소수점 둘째 자리까지 반올림

정답 코드

SELECT s.user_id,
       CASE WHEN c.action IS NULL THEN 0.00
       	ELSE ROUND(SUM(c.action = "confirmed")/COUNT(*),2)
       	END AS confirmation_rate
FROM signups s LEFT JOIN confirmations c ON s.user_id = c.user_id
GROUP BY s.user_id

개선점

  1. CASE WHEN c.action IS NULL은 잘못된 위치
    1. GROUP BY 이후 집계 함수(SUM, COUNT)를 쓸 때
      CASE는 그룹 전체에 대해 한 번만 적용되지 않음
    2. 그래서 일부 DB에서는 이 코드가 정상 작동하더라도, 로직상으로는 “한 행만 NULL이면 전체가 0 처리”될 수 있음

      IFNULL() 이 훨씬 안전하고 직관적
  2. COUNT(*) 대신 COUNT(c.action) 사용
    1. COUNT(*)는 NULL도 포함해 세기 때문에, LEFT JOIN 결과에서 Confirmations가 전혀 없는 사용자도 1로 계산될 수 있음
    2. 반면, COUNT(c.action)은 NULL은 세지 않으므로 → 요청이 없을 경우 0으로 정확히 계산됨
  3. 문자열 리터럴은 'confirmed'로 작성
    1. MySQL은 " "를 문자열로 인식하지만, 표준 SQL에서는 ' '이 맞음 → LeetCode나 다른 SQL엔진에서의 이식성을 고려하면 ' '을 쓰는 게 좋음

개선된 코드

SELECT 
    s.user_id,
    ROUND(
    	IFNULL(SUM(c.action = 'confirmed') / COUNT(c.action), 0),
        2
    ) AS confirmation_rate
FROM Signups s LEFT JOIN Confirmations c ON s.user_id = c.user_id
GROUP BY s.user_id

코드 설명

LEFT JOIN 모든 가입자 유지 (확인 요청 없는 사용자도 포함)
SUM(c.action = 'confirmed') MySQL에서는 조건이 참이면 1, 거짓이면 0 → confirmed 개수
COUNT(c.action) 전체 요청 개수
IFNULL(..., 0) 요청이 전혀 없는 경우 → 0으로 설정
ROUND(..., 2) 두 자리 소수로 반올림
GROUP BY s.user_id 사용자별 계산