1. Problem
각 사원의 보너스를 계산해야 한다. 보너스 지급 조건은 두 가지가 동시에 충족되어야 한다.
- 사원 번호(employee_id)가 홀수일 것
- 사원 이름(name)이 'M'으로 시작하지 않을 것 이 조건에 부합하면 급여의 100%를 보너스로 지급하고, 그렇지 않으면 0을 지급한다. 숫자 데이터의 산술 연산과 문자열의 패턴 매칭을 동시에 활용해야 하는 문제이다.
2. Solution
논리적으로 데이터를 분리하는 UNION ALL 방식과 연산 효율을 높인 CASE WHEN 방식으로 풀이할 수 있다.
[방법 A] UNION ALL을 활용한 집합 결합 보너스 대상자와 비대상자를 각각 추출하여 수직으로 통합한다. 각 조건의 여집합을 명확히 구분해야 데이터 중복이나 누락을 방지할 수 있다.
[방법 B] CASE WHEN을 활용한 조건 분기 (권장) 하나의 SELECT 문 내에서 행별로 조건을 판별한다. 테이블을 한 번만 스캔(Single Scan)하므로 대용량 데이터에서 훨씬 효율적이다.
3. Takeaway (조건부 로직과 연산 효율성)
복잡한 비즈니스 로직을 SQL로 구현할 때 분석가가 고려해야 할 객관적 지표를 정립한다.
- Single Scan의 이점: UNION ALL은 논리적으로 명확하지만 테이블을 최소 두 번 읽어야 한다. 반면 CASE WHEN은 데이터를 한 번만 훑으며 결과를 도출하므로 I/O 비용을 획기적으로 줄일 수 있다.
- 논리 연산의 정밀도: AND 조건의 반대(여집합)가 OR 조건이 된다는 논리 구조(드 모르간의 법칙)를 정확히 이해해야 집합 결합 시 오류를 피할 수 있다.
- 문자열 패턴 매칭: LIKE 'M%'와 NOT LIKE 'M%'를 활용해 특정 접두사를 필터링하는 기법은 고객 세그먼트 분류 등 실무에서 매우 빈번하게 활용된다.
https://leetcode.com/problems/calculate-special-bonus/
Table: Employees
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| employee_id | int |
| name | varchar |
| salary | int |
+-------------+---------+
employee_id is the primary key (column with unique values) for this table.
Each row of this table indicates the employee ID, employee name, and salary.
Write a solution to calculate the bonus of each employee.
The bonus of an employee is 100% of their salary
if the ID of the employee is an odd number and the employees name does not start with the character 'M'.
The bonus of an employee is 0 otherwise.
Return the result table ordered by employee_id.
The result format is in the following example.
Example 1:
Input:
Employees table:
+-------------+---------+--------+
| employee_id | name | salary |
+-------------+---------+--------+
| 2 | Meir | 3000 |
| 3 | Michael | 3800 |
| 7 | Addilyn | 7400 |
| 8 | Juan | 6100 |
| 9 | Kannon | 7700 |
+-------------+---------+--------+
Output:
+-------------+-------+
| employee_id | bonus |
+-------------+-------+
| 2 | 0 |
| 3 | 0 |
| 7 | 7400 |
| 8 | 0 |
| 9 | 7700 |
+-------------+-------+
Explanation:
The employees with IDs 2 and 8 get 0 bonus because they have an even employee_id.
The employee with ID 3 gets 0 bonus because their name starts with 'M'.
The rest of the employees get a 100% bonus.
📝 [SQL/Easy] Calculate Special Bonus
1. 문제 풀이의 핵심: "복합 조건의 분기 처리"
보너스를 받는 조건은 두 가지가 동시에(AND) 만족되어야 합니다.
- employee_id가 홀수일 것 (employee_id % 2 = 1)
- name이 'M'으로 시작하지 않을 것 (name NOT LIKE 'M%')
이 외의 모든 경우는 보너스가 0입니다.
2. 두 가지 풀이 전략 비교
① 나의 답안: UNION ALL
두 집단(보너스 대상자 vs 비대상자)을 각각 추출하여 합치는 방식입니다.
- 장점: 각 조건이 명확하게 분리되어 있어 논리 파악이 쉽습니다.
- 주의: UNION ALL을 쓸 때 두 쿼리의 조건이 서로 겹치지 않아야 중복 데이터가 발생하지 않습니다. 사용자님은 AND와 OR를 적절히 사용해 이 문제를 잘 해결하셨습니다.
WITH bonus AS ( # 테이블 이름 ALL했다가 예약어라서 안됨.
SELECT employee_id, salary AS bonus
FROM Employees
WHERE employee_id%2 = 1 AND name NOT LIKE 'M%'
UNION ALL
SELECT employee_id, 0 AS bonus
FROM Employees
WHERE employee_id%2 = 0 OR name LIKE 'M%'
)
SELECT *
FROM bonus
ORDER BY employee_id;
② 추천 방식: CASE WHEN (조건 분기)
하나의 SELECT 문 안에서 행별로 조건을 체크하여 값을 할당하는 방식입니다. 코드가 훨씬 간결해집니다.
SELECT
employee_id,
CASE
WHEN employee_id % 2 = 1 AND name NOT LIKE 'M%' THEN salary
ELSE 0 # 그게 아니면! 마법의 한단어 ELSE
END AS bonus
FROM Employees
ORDER BY employee_id;
3. 출제자의 의도 및 핵심 포인트
- 산술 연산과 문자열 패턴의 조합: 숫자 데이터의 특성(홀/짝)과 문자열 데이터의 패턴을 동시에 필터링할 수 있는가?
- 논리 연산자(AND/OR)의 정확성: "A이면서 B가 아닌 것"에 대한 여집합을 정확히 계산할 수 있는가?
- 정렬(Ordering): 최종 결과물이 employee_id 순으로 정렬되어야 함을 잊지 않았는가?
🚩 분석 팁
"조건이 복잡할수록 쿼리는 단순해야 한다."
UNION ALL은 논리적으로 완벽하지만, 테이블을 두 번 읽어야 한다는 단점이 있습니다.
CASE WHEN을 사용하면 테이블을 한 번만 읽으면서도(Full Scan 최소화) 가독성 좋은 코드를 작성할 수 있다.