join
두개의 테이블을 연결하여 데이터를 조회하는 방식
두 테이블의 공통된 정보(key)를 기준으로 조회
join의 종류

select * from table a
inner join table b
on a.column = b.column
1
on이 기준

select * from table a
left join table b
on a.column = b.column
이 뒤 where 붙음

select * from table a
right join table b
on a.column = b.column
SELECT *
from orders o
inner join users u
on o.user_id = u.user_id
기준이 되는 같은 컬럼 사용
2. checkins 테이블에 users 테이블 연결해보기
SELECT *
from checkins c
inner join users u
on c.user_id = u.user_id ;
SELECT *
from enrolleds e
inner join courses c
on e.course_id = c.course_id
SELECT comment, count(comment) as count
from checkins c2
inner join courses c
on c2.course_id = c.course_id
GROUP by c.title
SELECT*
from point_users pu
inner join users u
on pu.user_id =u.user_id
order by pu.`point` desc
SELECT u.name, count(u.name)
from orders o
inner join users u
on o.user_id =u.user_id
where u.email like "%naver.com%"
GROUP by u.name
1.Join 연습1
결제 수단 별 유저 포인트의 평균값 구해보기 point_users 에, orders 를 붙이기
SELECT o.payment_method, round(AVG(pu.point)) as avg_point
from point_users pu
inner join orders o
on pu.user_id =o.user_id
GROUP by o.payment_method
2.Join 연습2
결제하고 시작하지 않은 유저들을 성씨별로 세어보기 join 할 테이블: enrolleds 에, users 를 붙이기
SELECT name, count(*) as cnt_name
from enrolleds e
inner join users u
on e.user_id =u.user_id
where is_registered = 0
GROUP by u.name
order by cnt_name desc
3.Join 연습3
과목 별로 시작하지 않은 유저들을 세어보기
join 할 테이블: courses에, enrolleds 를 붙이기
SELECT c.course_id ,title ,count(*) as cnt_notstart
from courses c
inner join enrolleds e
on e.course_id =c.course_id
where e. is_registered = 0
GROUP by c.title
SELECT c.title, c2.week,COUNT(*)
from courses c
inner join checkins c2
on c.course_id = c2.course_id
GROUP by c2.week ,c.title
order by c.title ,c2.week
2.[퀴즈] Join 연습5
연습4번에서, 8월 1일 이후에 구매한 고객들만 발라내어 보세요!
join 할 테이블: courses에, checkins 를 붙이고+ checkins 에, orders 를 한번 더 붙이기!
SELECT c.title, c2.week,COUNT(*)
from courses c
inner join checkins c2
on c.course_id = c2.course_id
inner join orders o
on c2.user_id = o.user_id
where o.created_at >"2020-08-01"
GROUP by c2.week ,c.title
order by c.title ,c2.week
1.7월10일 ~ 7월19일에 가입한 고객 중,
포인트를 가진 고객의 숫자, 그리고 전체 숫자, 그리고 비율을 보고 싶어요
select count(point_user_id) as pnt_user_cnt,
count(*) as tot_user_cnt,
round(count(point_user_id)/count(*),2) as ratio
from users u
left join point_users pu on u.user_id = pu.user_id
where u.created_at between '2020-07-10' and '2020-07-20'

두개의 쿼리 문을 견결해서 조회하고 싶을 때 union을 사용한다
(쿼리문1)
union all
(쿼리문2)
union all 중복된값을 모두 보여준다(중복된값이 있으면 중복된 개수 전부 다 나온다)
enrolled_id별 수강완료(done=1)한 강의 갯수를 세어보고, 완료한 강의 수가 많은 순서대로 정렬해보기. user_id도 같이 출력
SELECT e.enrolled_id ,user_id,count(e2.done) as max_count
from enrolleds e
left join enrolleds_detail e2
on e.enrolled_id =e2.enrolled_id
where e2.done=1
GROUP by e.enrolled_id
ORDER by max_count desc
문제 유저 아이디 별 수강완료가 안된(done=0)된 갯수가 가장 많은 사람을 조회하기
조인할 테이블:endrolled_detail, endrolleds+ endrolleds, useres
조인할 필드 enrolled_id ,user_id
select u.user_id ,u.name ,e.done ,count(u.user_id) as cnt
from enrolleds_detail e
inner join enrolleds e2
on e.enrolled_id = e2.enrolled_id
INNER join users u
on e2.user_id = u.user_id
where e.done =0
group by u.user_id
order by cnt desc
[수업 목표]
서브쿼리란 쿼리안의 쿼리라는 이름으로 하위 쿼리의 결과를 상위 쿼리에서 사용
서브쿼리를 사용하지 않아도 데이터는 얻을 수 있지만 사용시 더 편하고 간단하게 데이터를 얻을 수 있다
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'
)
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;
(1) 밖의 select * from 문에서 데이터를 한줄한줄 출력하는 과정에서
(2) select 안의 subquery가 매 데이터 한줄마다 실행되는데
(3) 그 데이터 한 줄의 user_id를 갖는 데이터의 평균 좋아요 값을 subquery에서 계산해서
(4) 함께 출력해준다!
SELECT column1, column2
FROM (
SELECT column1, MAX(column2) AS max_column2
FROM table1
GROUP BY column1
) AS subquery_result
WHERE column2 = max_column2;
컬럼1과 컬럼2 는 이 서브쿼리의 결과에서 반환된다
일반적으로 이렇게 서브쿼리의 값이 외부퀴리에서 사용할때 이름을 맞춰줘야한다 이름을 다르게 쓰고 싶으면 as 주기
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
select * from point_users pu
where `point` >(SELECT avg( `point`) from point_users pu2)
select * from point_users pu
where pu.point >(SELECT avg(`point`) from point_users pu2
inner join users u
on pu.user_id = u.user_id
where u.name = "이**"
)
필요한 경우, Subquery 안에서 여러 테이블을 Join
select checkin_id ,course_id,user_id, likes,
(select avg(likes) from checkins c2
where c.course_id = c2.course_id) as course_avg from checkins c
select checkin_id ,course_id,user_id, likes,
(select avg(likes) from checkins c2
inner join courses c3
where c.course_id = c2.course_id) as course_avg from checkins c
inner join courses c3
on c.course_id = c3.course_id;
select course_id, count(distinct(user_id)) as cnt_checkins from checkins
group by course_id
select course_id, count(course_id) as cnt_checkins from checkins
group by course_id
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
쿼리를 작성하다보면 서브쿼리를 사용하는 경우가 많지만 그 경우가 많아질수록 가독성이 좋지 않다
또한 재사용이 불가능해 매번 같은 쿼리를 다시 작성해줘야 한다
이걸 해결하기 위해 with문 사용
이름을 가진 SubQuery를 정의한 후 사용하는 구문.대부분 RDBS에서 사용가능
게층형 쿼리 구현가능
WITH
cte1 AS (
SELECT column1, column2
FROM table1
WHERE condition1
),
cte2 AS (
SELECT column3, column4
FROM table2
WHERE condition2
)
with문은 임시테이블을 만드는 문법
위의 문법은 서브쿼리를 사용해 CTE1이라는 테이블을 만들고 그 테이블을 이용해 외부쿼리를 작성할 수 있다
SELECT *
FROM cte1
JOIN cte2 ON cte1.column1 = cte2.column3;
2개이상의 임시테이블을 만들고 싶으면
위의 코드 처럼 두개를 연속으로 만들면 된다
그러니까 일단 서브쿼리로 푼 다음에 그 서브쿼리를 이용해서 가상의 테이블을 만들고 그 테이블 끼리 조인하기
SUBSTRING_INDEX사용select SUBSTRING_INDEX(col1,"기준이될문자","몇번째") from table
-- 맨 처음은 1, 맨 뒤는 -1
SUBSTRING 사용select SUBSTRING(문자열, 출력을 하고싶은 첫 글자의 위치, 몇개의 글자를 출력하고 싶은지) from table
1.CASE WHEN~ END: 경우에 따라 원하는 값을 새 필드에 출력해보기
case는 파이썬이나 자바스크립트에서 사용하는 switch문과 비슷
select 뒤 조건에 따라 다르게 출력하고 싶을때 사용
SELECT column1,
CASE
WHEN column2 > 0 THEN 'Positive'
WHEN column2 < 0 THEN 'Negative'
ELSE 'Zero'
END AS result
FROM table_name;
1.평균 이상 포인트를 가지고 있으면 '잘 하고 있어요' / 낮으면 '열심히 합시다!' 표시하기!
(서브쿼리를 사용하지 않고 각 행의 개별적인 비교가 아니라 한개만 비교되서 맨위의 값 하나만 나온다)
select `point_user_id`, `point` ,
CASE when `point` >(select avg(point) from point_users pu )then "잘하고 있어요!" else "열심히 합시다" end as msg
from point_users pu
select SUBSTRING_INDEX(email,"@",-1) as domain, count(*)as cnt_domain
from users u
group by domain
select *
from checkins c
where c.comment like "%화이팅%"
select enrolled_id , count(*) as done_cnt, (select COUNT(*)
from enrolleds_detail eds
where ed.enrolled_id = eds.enrolled_id
group by enrolled_id ) as totoal_cnt
from enrolleds_detail ed
where done =1
group by enrolled_id
(여기서 중요한거
서브쿼리 쓸때 둘이 메인과 서브가 둘이 같은 테이블을 참조한다고 해도 둘이 같은 거라고
where문을 써줘야 한다)
서브쿼리에서 참조하는 외부 테이블의 특정 행을 가져올 필요가 있는 경우 WHERE 절을 사용하여 조건을 명시
SELECT 문의 FROM 절에 서브쿼리를 사용할 때, WHERE 절을 통해 메인 쿼리와 서브쿼리 간의 연결 조건을 설정
select enrolled_id ,
(select count(*) from enrolleds_detail ed where e.enrolled_id = ed.enrolled_id and done !=0)
as done_cnt,
(select count(*) from enrolleds_detail ed where e.enrolled_id = ed.enrolled_id) as total,
round((select count(*) from enrolleds_detail ed where e.enrolled_id = ed.enrolled_id and done !=0)
/
(select count(*) from enrolleds_detail ed where e.enrolled_id = ed.enrolled_id),2 )as ratio
from enrolleds e
근데 정답은 이거
with table1 as (
select enrolled_id, count(*) as done_cnt from enrolleds_detail
where done = 1
group by enrolled_id
), table2 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 table1 a
inner join table2 b on a.enrolled_id = b.enrolled_id
그리고 이거 내가 한 방식대로 풀면 done_cnt가 0인게 나온다 그거 말고는 다 맞는거 같은데...
(추가)
일단 먼저 서브쿼리 2개로 만든 다음에 이 서브쿼리를 이용해 with 문을 만들었다
with table1 as (
select enrolled_id, count(*) as done_cnt from enrolleds_detail
where done = 1
group by enrolled_id
), table2 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)
from table1 a
inner join table2 b
on a.enrolled_id = b.enrolled_id
오늘의 다짐' 데이터를 보고 싶은데 '오늘의 다짐' 좋아요의 수가, 본인이 받은 최대 좋아요수에 비해 얼마나 높고 낮은지가 궁금
select c.checkin_id, c.user_id, c.likes,
(select max(likes) from checkins c2
where c2.user_id = c.user_id) as avg_like_user
from checkins c;