빠르게 강의를 듣다 보니 벌써 4주차를 끝내고 쓰는 마지막주차 회고다. 제일 많이 쓰이고 조금은 심화된 내용을 다루는 4주차였는데 계속 반복해서 쿼리를 작성해보고 연습을 많이 해봐야 알 것같다! 끝나도 끝난게 아닌..
이번 주차에서 다룰 내용은 Subquery(서브쿼리)의 사용 방법에 대해서 배운다.
또한 실전에서 유용한 SQL문법을 더 배워보고 실전과 같은 데이터분석을 진행한다.
Subquery란? 쿼리 안의 쿼리라는 의미이며 하위 쿼리 결과를 상의 쿼리에서 사용하면 SQL쿼리가 훨씬 간단해진다!
(예시) kakaopay로 결제한 유저들의 정보 보기
→ 우선 users와 orders의 inner join으로 본다.
select u.user_id, u.name, u.email from users u
inner join orders o on u.user_id = o.user_id
where o.payment_method = 'kakaopay'
이렇게도 볼 수 있지만!
select user_id from orders
where payment_method = 'kakaopay'
select u.user_id, u.name, u.email from users u
where u.user_id in (
select user_id from orders
where payment_method = 'kakaopay'
)
Subquery는 where,select,from 절에서 유용하게 쓰인다!
where은 조건문이다. 서브쿼리의 결과를 조건에 활용하는 방식으로 사용한다.
→ where 필드명 in (subquery)
(예시) 카카오페이로 결제한 주문건 유저들만 유저 테이블에서 출력하기
select * from users u
where u.user_id in (select o.user_id from orders o
where o.payment_method = 'kakaopay');
Select는 결과를 출력해주는 부분이다. 기존 테이블에 함께 보고싶은 통계 데이터를 손쉽게 붙이는 것에 사용한다.
→select 필드명, 필드명, (subquery) from ..
(예시) 앞서 보았던거처럼, '오늘의 다짐' 좋아요의 수가, 본인이 평소에 받았던 좋아요 수에 비해 얼마나 높고 낮은지 보기
1) 먼저 평균을 구해본다. user_id='4b8a10e6'를 예시로 한다.
select avg(likes) from checkins c2
where c2.user_id = '4b8a10e6'
2) 그러면 이렇게 표현할 수 있다!
select c.checkin_id, c.user_id, c.likes,
(select avg(likes) from checkins c2
where c2.user_id = c.user_id) as avg_like_user
from checkins c;
from은 내가 만든 select와 이미 있는 테이블을 join하고 싶을 때 주로 사용한다.
(예시) 유저 별 좋아요 평균을 구해보기
→ checkins 테이블을 user_id로 group by 한다!
select user_id, round(avg(likes),1) as avg_like from checkins
group by user_id
여기서 해당 유저 별 포인트를 보고싶다면?
→ 그러면, 포인트와 like의 상관정도를 알 수 있다.
select pu.user_id, a.avg_like, pu.point from point_users pu
inner join (
select user_id, round(avg(likes),1) as avg_like from checkins
group by user_id
) a on pu.user_id = a.user_id
1. Where 절에 들어가는 Subquery 연습해보기
[연습1] 전체 유저의 포인트의 평균보다 큰 유저들의 데이터 추출하기
(힌트)point_users 테이블을 이용해서 avg를 구하고, 다시 point_users와 조인하기!
select * from point_users pu
where pu.point > (select avg(pu2.point) from point_users pu2);
👉위와 같이, 같은 테이블을 Subquery로 사용할 수도 있다.
[연습2]이씨 성을 가진 유저의 포인트의 평균보다 큰 유저들의 데이터 추출하기
(힌트)위 구문의 서브쿼리 내에서 users와 inner join하기
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 name = '이**')
👉필요한 경우, Subquery 안에서 여러 테이블을 Join 할수도 있다.
2. Select 절에 들어가는 Subquery 연습해보기
[연습1] 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
[연습2] checkins 테이블에 과목명별 평균 likes수 필드 우측에 붙여보기
연습1에서 실습했던 것에, courses 테이블을 join하기!
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;
3. From 절에 들어가는 Subquery 연습해보기
[준비1] course_id별 유저의 체크인 개수를 구해보기!
→ checkins 테이블을 course_id로 group by 하고 distinct로 유저를 세본다.
select course_id, count(distinct(user_id)) as cnt_checkins from checkins
group by course_id
[준비2] course_id별 인원을 구해보기!
select course_id, count(*) as cnt_total from orders o
group by course_id
[진짜 하고 싶은 것] course_id별 like 개수에 전체 인원을 붙이기
→ 준비1과 준비2를 inner join하기
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
[한 걸음 더] 퍼센트를 나타내기
select a.course_id, 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
[반 걸음 더] 강의 제목 나타내기
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 o
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(*) as cnt_total from orders o
group by course_id
), table2 as (
select course_id, count(distinct(user_id)) as cnt_checkins from checkins
group by course_id
)
select c.title,
b.cnt_checkins,
a.cnt_total,
( b.cnt_checkins/a.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
select user_id, email, SUBSTRING_INDEX(email, '@', 1) from users
👉 @를 기준으로 텍스트를 쪼개고, 그 중 첫 번째 조각을 가져오라는 뜻!
select user_id, email, SUBSTRING_INDEX(email, '@', -1) from users
👉@를 기준으로 텍스트를 쪼개고, 그 중 마지막 조각을 가져오라는 뜻!
select order_no, created_at, substring(created_at,1,10) as date from orders
(예시) 일별로 몇 개씩 주문이 일어났는지 살펴보자!
select substring(created_at,1,10) as date, count( \* ) as cnt_date from orders
group by date
(예시)포인트 보유액에 따라 다르게 표시해주기
10000점 보다 높으면 '잘하고 있어요!', 평균보다 낮으면 '조금 더 달려주세요!'
select pu.point_user_id, pu.point,
case
when pu.point > 10000 then '잘 하고 있어요!'
else '조금 더 달려주세요!'
END as '구분'
from point_users pu
select pu.point_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 level, count(*) as cnt from (
select pu.point_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
) a
group by level
with table1 as (
select pu.point_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 level, count(*) as cnt from table1
group by level
[퀴즈1]이메일 도메인별 유저의 수 세어보기
select domain, count(*) as cnt from (
select SUBSTRING_INDEX(email,'@',-1) as domain from users
) a
group by domain
[퀴즈2] '화이팅'이 포함된 오늘의 다짐만 출력해보기
select * from checkins
where comment like '%화이팅%'
[퀴즈3] 수강등록정보(enrolled_id)별 전체 강의 수와 들은 강의의 수 출력해보기
(힌트) subquery 두 개를 만들어놓고, inner join!
살펴볼 테이블: enrolled_detail
done_cnt는 들은 강의의 수(done=1),
total_cnt는 전체 강의의 수
with lecture_done as (
select enrolled_id, count(\*) as cnt_done from enrolleds_detail ed
where done = 1
group by enrolled_id
), lecture_total as (
select enrolled_id, count(\*) as cnt_total from enrolleds_detail ed
group by enrolled_id
)
select a.enrolled_id, a.cnt_done, b.cnt_total from lecture_done a
inner join lecture_total b on a.enrolled_id = b.enrolled_id
[퀴즈4] 수강등록정보(enrolled_id)별 전체 강의 수와 들은 강의의 수, 그리고 진도율 출력해보기
(힌트)진도율 = (들은 강의의 수 / 전체 강의 수)
with lecture_done as (
select enrolled_id, count(*) as cnt_done from enrolleds_detail ed
where done = 1
group by enrolled_id
), lecture_total as (
select enrolled_id, count(*) as cnt_total from enrolleds_detail ed
group by enrolled_id
)
select a.enrolled_id,
a.cnt_done,
b.cnt_total,
round(a.cnt_done/b.cnt_total,2) as ratio
from lecture_done a
inner join lecture_total b on a.enrolled_id = b.enrolled_id
select enrolled_id,
sum(done) as cnt_done,
count(*) as cnt_total
from enrolleds_detail ed
group by enrolled_id
이렇게 해도 같은 결과가 나온다!