SQL

SQL 4주차

seulki_lim 2023. 3. 21. 20:40

4주차

Subquery

  • 쿼리 안의 쿼리
  • 원하는 데이터를 더 쉽게 얻어올 수 있음

1. where 절에 들어가는 Subquery

  • 쿼리 결과를 조건으로 활용
select * from users u
where u.user_id in (
	select user_id from orders
	where payment_method = 'kakaopay'
)

2. select 절에 들어가는 Subquery

  • 기존 테이블에 함께 보고싶은 통계 데이터를 손쉽게 붙일 때 사용
select c.checkin_id,
       c.user_id,
       c.likes,
       (
        select avg(likes) from checkins
		where user_id = c.user_id
       ) as avg_likes_user
from checkins c

3. from 절에 들어가는 Subquery

  • 내가 만든 Select를 원래 있던 테이블처럼 다른 테이블과 Join하고 싶을 때 사용
select pu.user_id, pu.point, a.avg_likes from point_users pu
inner join (
	select user_id, round(avg(likes),1) as avg_likes from checkins
	group by user_id
) a on pu.user_id = a.user_id

실습 (where, select)

1. 전체 유저의 포인트의 평균보다 큰 유저들의 데이터 추출하기

select * from point_users pu
where pu.point > (select avg(pu2.point) from point_users pu2)

 

2. 이씨 성을 가진 유저의 포인트의 평균보다 큰 유저들의 데이터 추출하기

select * from point_users pu
where pu.point > 
	(select avg(pu2.point) from point_users pu2
	 inner join users u on pu2.user_id = u.user_id
	 where u.name = '이**'
	)

 

3. checkins 테이블에 course_id별 평균 likes수 필드 우측에 붙여보기

select checkin_id, course_id, user_id, likes, 
(
	select round(avg(c2.likes),1) from checkins c2
	where c.course_id = c2.course_id
) as course_avg
from checkins c

 

4. checkins 테이블에 과목명별 평균 likes수 필드 우측에 붙여보기

select checkin_id, c3.title, user_id, likes, 
(
	select round(avg(c2.likes),1) from checkins c2
	where c.course_id = c2.course_id
) as course_avg
from checkins c
inner join courses c3 on c.course_id = c3.course_id

 

실습 (from, inner join)

1. course_id별 유저의 체크인 개수를 구해보기

select course_id, count(DISTINCT(user_id)) as cnt_checkins from checkins
group by course_id
  • 중복 제거하고 셀 때 distinct

2. course_id별 인원을 구해보기

select course_id, count(*) as cnt_total from orders
group by course_id

 

3. course_id별 like 개수에 전체 인원을 붙이기

select a.course_id, b.cnt_checkins, a.cnt_total from
(
	select course_id, count(*) as cnt_total from orders
	group by course_id
) a
inner join(
	select course_id, count(DISTINCT(user_id)) as cnt_checkins from checkins
	group by course_id
) b on a.course_id = b.course_id

 

4. 전체 중 얼마나 like하는지 붙이고 강의 제목도 나타내기

select c.title, b.cnt_checkins, a.cnt_total, (b.cnt_checkins/a.cnt_total) as ratio from
(
	select course_id, count(*) as cnt_total from orders
	group by course_id
) a
inner join(
	select course_id, count(DISTINCT(user_id)) as cnt_checkins from checkins
	group by course_id
) b on a.course_id = b.course_id
inner join courses c on a.course_id = c.course_id

 

with절

with table1 as (
	select course_id, count(distinct(user_id)) as cnt_checkins from checkins
	group by course_id
), table2 as (
	select course_id, count(*) as cnt_total from orders
	group by course_id 
)
select c.title,
       a.cnt_checkins,
       b.cnt_total,
       (a.cnt_checkins/b.cnt_total) as ratio
from table1 a
inner join table2 b on a.course_id = b.course_id
inner join courses c on a.course_id = c.course_id
  • 서브쿼리가 계속 붙으면 inner join 안쪽이 헷갈릴 수 있어 table로 치환하는 느낌

실전에서 유용한 SQL 문법 (문자열)

  • SUBSTRING_INDEX(email, '@', 1) : @ 앞의 내용 가져오기
  • SUBSTRING_INDEX(email, '@', -1) : @ 뒤의 내용 가져오기
  • SUBSTRING(created_at, 1, 10) : 문자열의 시작부터 10자리 가져오기

실전에서 유용한 SQL 문법 (Case)

select pu.user_id, pu.point,
	   (case when pu.point > 10000 then '잘 하고 있어요'
	    else '조금만 더 파이팅' end) as msg
  from point_users pu
  • 10000점을 기준으로 다른 메시지를 새 필드에 출력하기
with table1 as(
	select pu.user_id, pu.point,
			   (case when pu.point > 10000 then '1만 이상'
			   		 when pu.point > 5000 then '5천 이상'
			    	 else '5천 미만' end) as level
		  from point_users pu
)
select a.level, count(*) as cnt from table1 a
group by a.level
  • with절로 묶으면 table을 아래에서 계속 사용 할 수 있음

 

퀴즈

1. 평균 이상 포인트를 가지고 있으면 '잘 하고 있어요' / 낮으면 '열심히 합시다!' 표시하기!

select p.point_user_id, p.point,
	(case when p.point > (select avg(pu.point) from point_users pu) then '잘 하고 있어요!'
	 else '열심히 합시다!' end
	) as msg
from point_users p

 

2. 이메일 도메인별 유저의 수 세어보기

select substring_index(email,'@',-1) as domain, count(*) from users
group by domain

 

3. '화이팅'이 포함된 오늘의 다짐만 출력해보기

select * from checkins
where comment like '%화이팅%'

 

4. 수강등록정보(enrolled_id)별 전체 강의 수와 들은 강의의 수 출력해보기

select a.enrolled_id, a.done_cnt, b.total_cnt from
(
	select enrolled_id, count(*) as done_cnt from enrolleds_detail
	where done = 1
	group by enrolled_id
) a
inner join
(
	select enrolled_id, count(*) as total_cnt from enrolleds_detail
	group by enrolled_id
) b on a.enrolled_id = b.enrolled_id


#with절 사용
with done as (
	select enrolled_id, count(*) as done_cnt from enrolleds_detail
	where done = 1
	group by enrolled_id
), total as (
	select enrolled_id, count(*) as total_cnt from enrolleds_detail
	group by enrolled_id
)
select a.enrolled_id, a.done_cnt, b.total_cnt from done a
inner join total b on a.enrolled_id = b.enrolled_id

 

5. 수강등록정보(enrolled_id)별 전체 강의 수와 들은 강의의 수, 그리고 진도율 출력해보기

with done as (
	select enrolled_id, count(*) as done_cnt from enrolleds_detail
	where done = 1
	group by enrolled_id
), total as (
	select enrolled_id, count(*) as total_cnt from enrolleds_detail
	group by enrolled_id
)
select a.enrolled_id, a.done_cnt, b.total_cnt, round((a.done_cnt/b.total_cnt),2) as ratio from done a
inner join total b on a.enrolled_id = b.enrolled_id