[데이터분석] 부트캠프 TIL

20260106 TIL

myun0506 2026. 1. 6. 21:05

Today I Learn

: 코드카타, 문풀날 연습문제, 복습문제, 파이썬 세션, SQL 사전퀘스트 Lv5

 

- 코드카타

 

- 문제 1  프로그래머스 (#26)  입양 시각 구하기(1)

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59412

2. 정답 코드:

select hour(datetime) as hour, count(*) as count
from animal_outs 
where hour(datetime) between 9 and 19
group by hour
order by hour
  • hour(): datetime 타입에서 시간만 추출하는 함수
select 
    date_format(datetime, '%H') as hour, 
    count(*) as count 
from animal_outs 
where date_format(datetime, '%H') between 9 and 19 
group by hour 
order by hour
  • date_format(datetime, '%h'): 소문자 h를 사용하면 0-12시 사이만
  • date_format(datetime, '%H'): 대문자 H를 사용하면 0-23시 사이까지
  • date_format(datetime, '%h %p'): 소문자 h 사용으로 0-12시 사이 표시하고 소문자 p도 사용하여 AM PM 표시
  • but, 이 코드는 문자열을 반환하므로 where 절에서 문자열과 숫자의 비교가 발생함!
    • mysql은 내부적으로 이 비교를 수행할 때 문자열을 숫자로 바꾸긴 함
    • but 데이터 타입을 제어하는 습관을 길러야함!! 
    • → +0이나 cast를 사용하는 방법!
SELECT 
    CAST(DATE_FORMAT(DATETIME, '%H') AS UNSIGNED) AS HOUR,
    COUNT(*) AS COUNT
FROM ANIMAL_OUTS
WHERE CAST(DATE_FORMAT(DATETIME, '%H') AS UNSIGNED) BETWEEN 9 AND 19
GROUP BY HOUR
ORDER BY HOUR;

 

  • cast(... as unsigned): 문자열을 숫자형(Integer)으로 변환
SELECT 
    DATE_FORMAT(DATETIME, '%H') + 0 AS HOUR,
    COUNT(*) AS COUNT
FROM ANIMAL_OUTS
WHERE DATE_FORMAT(DATETIME, '%H') BETWEEN '09' AND '19'
GROUP BY HOUR
ORDER BY HOUR;

 

- 문제 2 프로그래머스 (#27) 진료과별 총 예약 횟수 출력하기  

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/132202

2. 정답 코드

select MCDP_CD as '진료과코드', count(distinct PT_NO) as '5월예약건수'
from appointment 
where APNT_YMD between '2022-05-01' and '2022-05-31' 
group by MCDP_CD
order by count(distinct PT_NO) asc, MCDP_CD asc
select MCDP_CD as '진료과코드', count(distinct PT_NO) as '5월예약건수'
from appointment 
where APNT_YMD like '2022-05%'
group by MCDP_CD
order by count(distinct PT_NO) asc, MCDP_CD asc

 

- 문제 3 프로그래머스 (#28) 12세 이하인 여자 환자 목록 출력하기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/132201

2. 정답 코드

select PT_NAME, PT_NO, GEND_CD, AGE, ifnull(TLNO,'NONE') as TLNO
from patient 
where age <= 12 and gend_cd = 'W'
order by age desc, pt_name asc

 

- 문제 4 프로그래머스 (#29) 인기있는 아이스크림  

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/133024

2. 정답 코드

select flavor
from (
select SHIPMENT_ID, FLAVOR, TOTAL_ORDER
from first_half 
group by flavor
order by total_order desc, shipment_id asc ) as subquery
select FLAVOR
from first_half 
order by total_order desc, shipment_id asc

 

- 문제 5 프로그래머스 (#30) 자동차 종류 별 특정 옵션이 포함된 자동차 수 구하기  

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/151137

2. 정답 코드

select 
    CAR_TYPE, count(*) as CARS
from car_rental_company_car 
where options like '%통풍시트%'
    or options like '%열선시트%'
    or options like '%가죽시트%'
group by CAR_TYPE
order by CAR_TYPE
select 
    CAR_TYPE, count(*) as CARS
from car_rental_company_car 
where options regexp '통풍시트|열선시트|가죽시트'
group by CAR_TYPE
order by CAR_TYPE
  • regexp '목록1|목록2|목록3...': 목록 여러개 중 하나라도 포함하는지 확인

- 문제 6 프로그래머스 (#40) 루시와 엘라 찾기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59046

2. 정답 코드:

select animal_id, name, sex_upon_intake 
from animal_ins 
where name in ('Lucy','Ella','Pickle','Rogan','Sabrina','Mitty')

 

- 문제 7 프로그래머스 (#41) 조건에 맞는 도서 리스트 출력하기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/144853

2. 정답 코드:

select book_id, 
       date_format(published_date,'%Y-%m-%d') as published_date
from book 
where year(published_date) = '2021' and category = '인문'
order by published_date asc

 

- 문제 8 프로그래머스 (#42) 평균 일일 대여 요금 구하기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/151136

2. 정답 코드:

select round(avg(daily_fee),0) as average_fee
from car_rental_company_car
where car_type = 'SUV'

 

- 문제 9 프로그래머스 (#45) 3월에 태어난 여성 회원 목록 출력하기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/131120

2. 정답 코드:

select member_id, member_name, gender, 
       date_format(date_of_birth,'%Y-%m-%d')
from member_profile 
where month(date_of_birth) = '03' 
    and gender = 'W' 
    and tlno is not null
order by member_id asc
  • month() 함수는 정수타입을 반환함 
    • 근데 내가 문자열 '03'과 비교하려 했기 때문에 문자열을 숫자로 변환하려 시도할 것
    • but month()의 결과는 앞에 0이 붙지 않은 순수한 숫자임
    • 만약 mysql이 문자열 기준으로 비교했다면 '3' = '03'은 거짓(False)이 되어 아무 결과도 안 나왔을 것
    • but mysql이 숫자 중심으로 변환해서 비교했기 때문에 '03'이든 '3'이든 상관 없이 숫자 3으로 취급되어 성공적으로 데이터를 찾아낸 것
select member_id, member_name, gender, 
       date_format(date_of_birth,'%Y-%m-%d')
from member_profile 
where month(date_of_birth) = 3
    and gender = 'W' 
    and tlno is not null
order by member_id asc

 

- 문제 10 프로그래머스 (#47) 모든 레코드 조회하기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59034

2. 정답 코드:

select *
from animal_ins
order by animal_id asc

 

- 문제 11 (#54)

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59415

2. 정답 코드:

select datetime 
from animal_ins 
order by datetime desc 
limit 1

 

- 문제 12 (#56)

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/157343

2. 정답 코드:

select *
from car_rental_company_car
where options regexp '네비게이션'
order by car_id desc

 

- 문제 13 (#32)

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/144855

2. 정답 코드:

select b.category, sum(sales) as total_sales
from book b
join book_sales bs
on b.book_id = bs.book_id
where bs.sales_date like '2022-01%'
group by b.category
order by b.category

 

- 문제 14 (#33) 

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/131533

2. 정답 코드: 

select p.product_code, sum(p.price*os.sales_amount) as sales
from product p 
join offline_sale os 
on p.product_id = os.product_id
group by 1
order by 2 desc, 1 asc

 

- 문제 15 (#34) 

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59043

2. 정답 코드: 

select i.animal_id, i.name
from animal_ins i
join animal_outs o 
on i.animal_id = o.animal_id 
where o.datetime <= i.datetime 
order by i.datetime asc

 

- 문제 16 (#35) 

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59411

2. 정답 코드:

select animal_id, name 
from (
select i.animal_id, i.name, 
       datediff(o.datetime,i.datetime) as days 
from animal_ins i 
join animal_outs o 
on i.animal_id = o.animal_id 
order by datediff(o.datetime,i.datetime) desc 
limit 2
) a

 

- 문제 17 (#36) 

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/59045

2. 정답 코드:

select i.animal_id, i.animal_type, i.name
from animal_ins i
join animal_outs o
on i.animal_id = o.animal_id
where (i.sex_upon_intake like ('Intact%'))
and (o.sex_upon_outcome like ('Spayed%') 
or o.sex_upon_outcome like ('Neutered%'))

 

- 문제 18 (#34) 

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/144854

2. 정답 코드:

select 
    b.book_id, 
    a.author_name, 
    date_format(b.published_date,'%Y-%m-%d') as published_date
from book b
join author a 
on b.author_id = a.author_id 
where b.category = '경제'
order by 3

 

 

- 사전퀘스트 Lv 5. 가장 많이 팔린 품목은?

  • 내 문제점 (by GEMINI)
    • 문법적 순서
      • join 앞에 where가 온 것
      • 무조건 from-join이 한 세트이고 그 뒤에 필터 where가 와야함
    • 스코프(Scope)의 한계와 복잡도
      • 바깥쪽 서브쿼리의 값을 안쪽 서브쿼리가 참조하려고 하는데
        • 이렇게 서로 다른 층위의 데이터를 연결하면 (상관 서브쿼리)
        • 데이터가 많아질 때 기하급수적으로 느려짐
    • Group By의 남용
      • 이미 where절에서 최댓값이 행들만 필터링했기 때문에 
        • 또 group by를 할 필요가 없음
  • 가장 확실한 해결방안
    • CTE 사용하기!!!
  • 문제 2 (각 제품 카테고리별로 가장 많이 팔린 제품의 이름과 총 판매량을 조회하기)
with totalsold_t as (
select 
	p.product_name, 
	p.category, 
	sum(o.quantity) as totalsold
from basic.products p 
join basic.orders3 o on p.id = o.productid
group by 1, 2
),
high_totalsold_t as (
select 
	old_t.category,
	old_t.product_name,
	row_number() over (partition by old_t.category order by old_t.totalsold desc)
	 as rnk, 
	old_t.totalsold
from totalsold_t old_t
) 
select category, product_name, totalsold 
from high_totalsold_t as high 
where rnk = 1

 

- 사전퀘스트 Lv.5 예산이 가장 큰 프로젝트는?

  • 내 문제점
    • subquery를 만들지 않아도 됐음
      • 어차피 그대로 employees2 테이블 사용하면 됐는데...
    • where 절의 scope 위반
      • 부서를 연결지어주기 위해서 where절을 추가했지만 옳지 않았던 where절의 사용...
        • max()를 불러오는 서브쿼리 내에서 where절을 사용해서 외부쿼리의 부서와 연결지어주면 됨!
# 내가 작성한 CTE 쿼리
with subquery as (
select 
	e2.department, 
	max(e2.salary) as sal
from basic.employees2 e2 
group by e2.department
)
select e.name, e.department, e.salary 
from basic.employees2 e 
join subquery s
on e.department = s.department
and s.sal = e.salary


# 내가 쓴 망한 쿼리
select e.name, e.department, e.salary 
from basic.employees2 e 
where e.salary = ( 
	select max(subquery.sal) 
	from 
		(select 
			e2.department, 
			max(e2.salary) as sal
		 from basic.employees2 e2 
		 group by e2.department
		 ) as subquery
	) 
and e.department = subquery.department

# 정답 쿼리
select e.name, e.department, e.salary 
from basic.employees2 e 
where e.salary = ( 
	select max(e2.salary) 
	from basic.employees2 e2
	where e.department = e2.department
)

 

 

 

- 파이썬 연습문제

 

https://myun0506.tistory.com/52

 

입출력, 변수, 문자열

1) 입출력 (Input / Output) sep 옵션 (기본값: sep = ' ' (공백))print(2026, 1, 4, sep="/") # 2016/1/4print("사과", "바나나", "포도", sep=", ") # 사과, 바나나, 포도print("2", "0", "2", "6", sep="") # 2026 end 옵션 (기본값: end = '

myun0506.tistory.com

 

  • 파이썬의 date 함수
print('2026','01','05',sep='/')
print(2026,1,5,sep='/')

from datetime import date
today = str(date.today())
clean = today.replace("-","/")
print(clean)
num = input()
num2 = int(num.replace(',',''))
print(num2*2)

num3 = int("".join(n for n in num if n.isdigit()))
print(num3*2)
  • 콤마와 공백 포함된 숫자 처리
    • cleaned
      • map은 두가지 인자를 전달받는데
        • 한개는 int라는 함수
        • 다른 한개는 (i.strip() for i in data) 라는 Iterable
    • cleaned2 
      • 여기선 한가지 묶음인데
        • for문을 돌면서 각각의 i를 strip함수에 적용시키고 int()함수까지 적용시키는 것
data = input().split(',') # 이미 리스트 반환 
cleaned = list(map(int,(i.strip() for i in data)))
avg = int(sum(cleaned) / len(cleaned))
print(avg)

cleaned2 = [int(i.strip()) for i in data] # 리스트 컴프리핸션 사용
avg2 = int(sum(cleaned2) / len(cleaned2)) 
print(avg2)
  • map 함수
    • map 함수가 반환하는 것은 map object인데, 이는 리스트는 아니지만 '한번에 하나씩 값을 꺼내줄 수 있는 봉투'와 같음
    • sum() 함수는 이 봉투 안에서 값이 다 나올 때까지 하나씩 꺼내서 더하기 때문에, 굳이 리스트라는 실제 바구니에 담아두지 않아도 합계를 구할 수 있는 것
    • map(function, iterable)
      • 첫번째 인자는 Collable(함수 객체)
      • 두번째 인자는 Iterable
  • split 함수
    • 공백을 기준으로 리스트를 만듦
  • 리스트 컴프리핸션 [ ] 
    • 그 안에 적힌 내용을 그대로 리스트의 요소로 집어 넣음
    • map은 그 자체로 숫자 꾸러미가 아니라 숫자를 만드는 기능일 뿐이라서
      • list()를 써서 기능을 실행시키거나 컴프리핸션으로 하나씩 꺼내 담아야 리스트가 됨
data = input()

data2 = map(int,data.replace(',','').split())
print(sum(data))

# 리스트 안에서 직접 정수 변환 반복
data3 = [int(i) for i in data.replace(',','').split()]
print(sum(data))

# map 객체 안의 결과물을 실제 리스트로 변환
data4 = list(map(int, data.replace(',','').split()))
print(sum(data)) # 이제 [100, 200] 형태가 되어 잘 작동합니다.

 

  • 파이썬의 문자열은 불변인 객체이므로 인덱싱을 통한 수정이나 할당 불가능함
s = "OXOXO"
# s[0] = "X" # 에러 발생 
print(s)
  • 시작과 끝 인덱스가 같으면 범위 없음
s = "Python"
print(f"'{s[0:0]}'") # 빈 문자열 '' 출력
# 설명: 시작과 끝 인덱스가 같으면 빈 문자열이 반환됩니다.

 

  • 인덱싱과 슬라이싱의 차이
s = "Python"

print(s[-1],type(s[-1])) 
print(s[-1:],type(s[-1:]))
# 결과는 같아보이지만 사실 차이가 있음
# 인덱싱을 한 것은 마지막 문자 하나를 불러오지만
# 슬라이싱을 한 것은 마지막 문자 하나를 포함한 부분 문자열을 불러옴 

list = [1,2,3,4,5] 
print(list[-1],type(list[-1])) # 5 int
print(list[-1:],type(list[-1:])) # [5] list
  • join() 
text = "A,B,C"
split_text = text.split(',')
result = "-".join(split_text)
print(result)
  • count() 
text = "banana"
print(text.count("na")) # 2
  • find() 
data = "name: 적토마"
idx = data.find(":")
name = data[idx+1:].strip()
print(name)

 

- 심화문제 (01_입출력)

1) `print("1", "2", sep="")``print("1" + "2")`의 결과가 같은가요? 다른가요?  
-> 같음 둘다 공백 없이 12가 나옴
2) `input()`으로 받은 `"10"`과 숫자 `10`은 무엇이 다른가요? `type()`으로 확인하세요.
-> input으로 받은 숫자는 문자열이므로 타입을 확인해보면 str이 나오고, 숫자 10은 숫자형이므로 확인해보면 int 정수타입이 나옴
4) `int("1,000")`이 실패하는 이유는 무엇인가요? 해결해 보세요.  
-> 문자열 1,000이 숫자만 있는 문자열이 아니고 콤마까지 있기 때문에 ValueError 발생함
-> int("1,000".replace(',','')라고 수정해야함
5) 공백 입력인데 `split(',')`를 쓰면 어떤 일이 생기나요?  
-> 여러 값이 입력되었더라도 하나의 값으로 인식하고 출력함
6) `a, b = map(int, input().split())`에서 `10 20 30`을 입력하면 어떤 에러가 나나요? 왜인가요?  
-> 6. 좌변에는 두개의 변수만 위치해있는데 오른쪽에서 세개의 값이 나오므로 ValueError가 나옴 : Too many values to unpack
9) `find`처럼 실패하면 -1을 주는 함수가 있다면, `try/except`가 꼭 필요할까요? (생각해 보기)  
-> x. index()함수는 찾는게 없으면 에러를 내서 try/except가 강제되지만, find()처럼 특정 값을 반환하면 if/else로 처리가 가능해지므로 따로 try/except가 필요하지 않음
10) 입력이 비었을 때(그냥 엔터) 어떻게 처리할지 만들어 보세요.
ans = input() 
if not ans :
  print("아무 값도 입력되지 않았습니다.")
else:
  print(ans)

 

- 심화문제 (02_변수)

8) 전역 변수 “읽기”와 “수정”의 차이를 한 줄로 정리하세요.
-> 전역 변수는 함수 내부에서 자유롭게 조회(읽기)할 수 있지만, 값을 변경(수정)하려면 global 키워드로 선언하거나 return을 통해 갱신해야 함.
-> 데이터 오염 방지를 위해! (return 선호)
10) (도전) 정수 2개를 입력받아 합/차/곱/나눗셈을 모두 출력하세요.
data = input() 
try:
  a,b = map(int,data.split())
  print(a+b)
  print(a-b)
  print(a*b)
  print(a/b)
except ValueError:
  print("옳지 않은 값을 입력했습니다.")
except ZeroDivisionError:
  print("0으로 나눌 수 없습니다.")

 

- 심화문제 (03_문자열)

  1. 경로 문자열에서 \n 문제가 생기는 예시와 raw 문자열로 해결하는 예시는?
# 경로 문자열에 \n과 같은 문자열이 있을 때 이를 경로로 받아들이는게 아니라 행 띄움 표시로 이해할 수 있으므로
# r"\n"식으로 작성하면 문자열 전체를 그대로 받아옴
path = r'\n\t\n\r\tn\r\t\t\t' 
print(path)
  1. (도전) 이메일에서 도메인만 뽑아서 대문자로 출력해 보세요.
email = 'minnie@gmail.com' 
ind1 = email.find('@') 
ind2 = email.find('.') 
domain = email[ind1+1:ind2] 
print(domain)

 

'[데이터분석] 부트캠프 TIL' 카테고리의 다른 글

20260108 TIL  (0) 2026.01.08
20260107 TIL  (1) 2026.01.07
20260105 TIL  (0) 2026.01.05
20260102 TIL  (1) 2026.01.02
20260101 TIL  (0) 2026.01.01