1. 조건문
- 조건에 따른 분기 처리가 필요한 경우에 사용 (Excel의 IF 함수 같은 느낌)
- "특정 조건이 참이면 A, 그렇지 않으면 B"
- 대표적으로 CASE_WHEN, IF 함수가 있음
- 예를 들어, 조건에 따라 다른 값을 표시하고 싶은 경우
- 1은 '일요일', 2는 '월요일', ... 6은 '토요일'로 출력하고 싶은 경우
조건문 함수가 자주 사용되는 상황
- 특정 카테고리를 하나로 합치는 전처리가 필요한 경우
- ex) 1,2,3학년은 '저학년', 4,5,6학년은 '고학년'으로 합치기 / 월~금은 '주중', 토~일은 '주말'로 합치기
👨🏫 이런 상황이 발생하는 이유?
• 보통 회사에서 데이터를 저장하는 부서와, 분석하는 부서가 나뉘어있음
• 그런데 저장할 때부터 특정 카테고리를 합쳐서 저장하면, 쪼개서 보는 게 아예 불가능함!
➡️그래서 데이터는 최대한 쪼개서 저장하고, 분석할 때 필요에 따라 변경하는 것이 더 유용함!
2. 조건문 함수
CASE WHEN
- 조건이 여러 개 있을 때 사용하기 좋음 (아래와 같이 나열하여 조건 지정)
SELECT
CASE
WHEN 조건1 THEN 조건1이_참일_경우_결과
WHEN 조건2 THEN 조건2가_참일_경우_결과
ELSE 그_외_조건일_경우_결과
END AS 새로운_컬럼_이름
- 예시) Rock(바위) 타입과 Ground(땅) 타입이 비슷하니까 Rock & Ground 라는 타입으로 합쳐서 보고 싶어
# 쿼리 작성하는 목표, 확인할 지표 : type이 Rock 또는 Ground일 경우 Rock&Ground로 수정
# 쿼리 계산 방법 : case when
# 데이터의 기간 :
# 사용할 테이블 : pokemon
# JOIN KEY :
# 데이터의 특징 : type1과 type2를 모두 고려해야 함!
-- ✅일단 type1에 대해 조건문 걸어서 출력
SELECT
*, -- 전체 열 출력해서 제대로 변환됐는지 체크!
CASE
WHEN type1 IN ("Rock", "Ground") THEN "Rock&Ground"
ELSE type1
END AS new_type1
FROM basic.pokemon;
-- ✅type2까지 반영해야 함!
SELECT
*,
CASE -- OR로 조건 추가
WHEN (type1 IN ("Rock", "Ground")) or (type2 IN ("Rock", "Ground")) THEN "Rock&Ground"
ELSE type1
END AS new_type1
FROM basic.pokemon
WHERE type2 IN ("Rock", "Ground") -- type2인 경우 출력해보기
- ⚠️조건 여러 개 쓸 때 순서에 주의해야 함!
- 어떤 row가 조건1, 조건2에 둘 다 해당할 경우, 앞에 적힌 조건1만 따라감 (WHEN에 한 번 걸린 row는 그 다음 WHEN으로 가지 않고 END로 가기 때문!)
- 즉, 이럴 경우에는 더 좁은 범위의 조건을 먼저 써야 함!
- 문자열 함수(특정 단어 추출)에서 이슈가 자주 발생함
- ex) 공격력(attack)이 50 이상이면 'Strong', 100 이상이면 'Very Strong', 그 이하면 'Weak'으로 분류
- 이런 경우는 아예 조건 동시에 안 겹치도록 MECE하게 설정하는 게 나을 수 있음 (ex. 50~99 / 100~ / ...)
IF
- 단일 조건일 경우 사용하기 좋음
- IF 안에 다시 IF를 써서 조건 여러 개 할 수도 있긴 하지만 비효율적!
SELECT
IF(조건1, True일_경우_결과, False일_경우_결과) AS 새로운_컬럼_이름
- 예시
3. 연습 문제
문제 1. 포켓몬의 'speed'가 70 이상이면 '빠름', 그렇지 않으면 '느림'으로 표시하는 새로운 컬럼 'Speed_Category'를 만들어 주세요
▶내 풀이 과정✅
1) 데이터 확인
2) 단일 조건이므로 IF 함수 사용

▶강사님 풀이 과정✅
1) 내 풀이와 동일하고, 아래 사항들만 약간 다름
- 데이터 확인 시, speed의 MIN, MAX도 확인해봄
▶전체 코드
SELECT
kor_name,
speed,
IF(speed>=70, '빠름', '느림') AS Speed_Category
FROM basic.pokemon
문제 2. 포켓몬의 'type1'에 따라 'Water', 'Fire', 'Electric' 타입은 각각 '물', '불', '전기'로, 그 외 타입은 '기타'로 분류하는 새로운 컬럼 'type_Korean'을 만들어 주세요
▶내 풀이 과정✅
1) 데이터 확인
- type1의 종류가 총 몇 개인지 DISTINCT로 확인해봄 = 17개 있네

2) 조건이 여러 개니까 CASE WHEN 조건문을 사용!

▶강사님 풀이 과정✅
1) 내 풀이와 동일! (DISTINCT로 확인하는 건 나만 했음)
▶전체 코드
# 쿼리 작성하는 목표, 확인할 지표 : type1을 한국어로 변경(총 4종류로)
# 쿼리 계산 방법 : case when
# 데이터의 기간 :
# 사용할 테이블 : pokemon
# JOIN KEY :
# 데이터의 특징 : type1이 총 17종류가 있음
SELECT
id,
kor_name,
type1,
CASE
WHEN type1="Water" THEN "물"
WHEN type1="Fire" THEN "불"
WHEN type1="Electric" THEN "전기"
ELSE "기타"
END AS type_Korean
FROM basic.pokemon
문제 3. 각 포켓몬의 총점(total)을 기준으로, 300 이하면 'Low', 301에서 500 사이면 'Medium', 501 이상이면 'High'로 분류해주세요
▶내 풀이 과정✅
1) 데이터 확인
- total 컬럼의 MIN, MAX 값을 확인해봄

2) 조건이 여러 개니까 CASE WHEN 조건문을 사용!

▶강사님 풀이 과정✅
1) 내 풀이와 거의 동일한데, 'Medium' 만들 때 BETWEEN 사용하셨음
▶전체 코드
# 쿼리 작성하는 목표, 확인할 지표 : total 값에 따라 등급 매기기
# 쿼리 계산 방법 : case when
# 데이터의 기간 :
# 사용할 테이블 : pokemon
# JOIN KEY :
# 데이터의 특징 : total = 180 ~ 680의 값을 가짐
SELECT
id,
kor_name,
total,
CASE
WHEN total >= 501 THEN "High"
WHEN total >= 301 THEN "Medium"
ELSE "Low"
END AS total_grade
FROM basic.pokemon;
-- 강사님 풀이
SELECT
id,
kor_name,
total,
CASE
WHEN total >= 501 THEN "High"
WHEN total BETWEEN 301 AND 500 THEN "Medium"
ELSE "Low"
END AS total_grade
FROM basic.pokemon;
문제 4. 각 트레이너의 배지 개수(badge_count)를 기준으로, 5개 이하면 'Beginner', 6개에서 8개 사이면 'Intermediate', 그 이상이면 'Advanced'로 분류해주세요.
▶내 풀이 과정✅
1) 데이터 확인
2) 조건이 여러 개니까 CASE WHEN 조건문을 사용!

▶강사님 풀이 과정✅
1) "Advanced"부터 입력하셨고, BETWEEN 사용하셨음.
▶전체 코드
# 쿼리 작성하는 목표, 확인할 지표 : badge_count 값에 따라 등급 매기기
# 쿼리 계산 방법 : case when
# 데이터의 기간 :
# 사용할 테이블 : trainer
# JOIN KEY :
# 데이터의 특징 :
SELECT
id,
name,
badge_count,
CASE
WHEN badge_count <= 5 THEN "Beginner"
WHEN badge_count <= 8 THEN "Intermediate"
ELSE "Advanced"
END AS badge_grade
FROM basic.trainer;
-- 강사님 풀이
SELECT
id,
name,
badge_count,
CASE
WHEN badge_count >= 9 THEN "Advanced"
WHEN badge_count BETWEEN 6 AND 8 THEN "Intermediate"
ELSE "Beginner"
END AS trainer_level
FROM basic.trainer;
문제 5. 트레이너가 포켓몬을 포획한 날짜(catch_date)가 '2023-01-01' 이후이면 'Recent', 그렇지 않으면 'Old'로 분류해주세요.
▶내 풀이 과정✅
1) 데이터 확인
- catch_date는 UTC 기준이므로 그냥 쓰면 안 됨 = catch_datetime을 KR 기준으로 바꿔서 사용하자

2) 단일 조건이므로 IF 함수 사용
- 한 SELECT문 안에서 안 되길래, 서브쿼리로 수행함

3) Old가 안 보여서 WHERE문 추가해서 출력해봤더니, Old는 없음!

▶강사님 풀이 과정✅
1) KR 기준 catch_date를 별도의 컬럼으로 추가하지 않고, 그냥 바로 IF 문에 넣음

2) 만약, 애초에 전부 "Recent"인 걸 알아서 그렇게 추가하고 싶었다면 아래와 같이 하면 됨!
- 모든 행에 동일한 값으로 채우고 싶을 때는 바로 AS 쓸 수 있음

▶전체 코드
# 쿼리 작성하는 목표, 확인할 지표 : catch_date에 따라 분류
# 쿼리 계산 방법 : if
# 데이터의 기간 :
# 사용할 테이블 : trainer_pokemon
# JOIN KEY :
# 데이터의 특징 :
-- catch_datetime은 이름과 달리 TIMESTAMP 타입임
-- catch_date는 UTC 기준이므로 직접 만든 catch_date_kr을 사용
SELECT
*,
IF(catch_date_kr>"2023-01-01", "Recent", "Old") AS catch_date_class
FROM(
SELECT
catch_date, -- UTC 기준 날짜
catch_datetime, -- 한국 기준 날짜
DATE(catch_datetime, "Asia/Seoul") AS catch_date_kr,
FROM basic.trainer_pokemon
)
-- 강사님 풀이
SELECT
id,
trainer_id,
pokemon_id,
IF(DATE(catch_datetime, "Asia/Seoul")>="2023-01-01", "Recent", "Old") AS recent_or_old,
"Recent" AS recent_value -- 전체 행에 같은 값 채우고 싶은 경우
FROM basic.trainer_pokemon
문제 6. 배틀에서 승자(winner_id)가 player1_id와 같으면 'Player 1 Wins', player2_id와 같으면 'Player 2 Wins', 그렇지 않으면 'Draw'로 결과가 나오게 해주세요
▶내 풀이 과정✅
1) 데이터 확인
2) 조건이 여러 개니까 CASE WHEN 조건문을 사용!

▶강사님 풀이 과정✅
1) 내 풀이와 동일!
▶전체 코드
# 쿼리 작성하는 목표, 확인할 지표 : winner_id를 보고 경기 결과 출력
# 쿼리 계산 방법 : case when
# 데이터의 기간 :
# 사용할 테이블 : battle
# JOIN KEY :
# 데이터의 특징 : winner_id에는 NULL도 있음 = 무승부
SELECT
* EXCEPT(battle_date, battle_datetime, battle_timestamp),
CASE
WHEN winner_id = player1_id THEN "Player 1 Wins"
WHEN winner_id = player2_id THEN "Player 2 Wins"
ELSE "Draw" -- NULL인 경우겠죠
END AS battle_result
FROM basic.battle
-- 강사님 풀이
🙏References
'SQL > BigQuery' 카테고리의 다른 글
[SQL] 11. 쿼리 가독성(WITH), 데이터 결과 검증 (0) | 2025.02.23 |
---|---|
[SQL] 10. 데이터 연결 (JOIN) (2) | 2025.02.21 |
[SQL] 8. 다양한 데이터 타입 (날짜 및 시간) (0) | 2025.02.13 |
[SQL] 7. 다양한 데이터 타입 (CAST, 문자열) (1) | 2025.02.12 |
[SQL] 6. SQL 쿼리 작성 팁, 오류 디버깅 (0) | 2025.02.11 |