본문 바로가기
SQL

부트캠프 개발일지(SQL 4주차)

by 썸맨 2023. 5. 12.

부트캠프 개발일지 - SQL4주차(23.05.12)

 

  • SubQuery : 쿼리 안에 들어가는 모든 쿼리문을 지칭(select/from/where절 모두 들어가기 가능), subquery내에 또 다른 subquery를 넣을 수 있음, 한 눈에 보기 좋게 잘 짜는게 핵심!, 실행 순서로는 가장 안의 subquery를 실행 후 결과를 가지고 점차 바깥의 쿼리문을 실행하는 순서

* subquery(where절) 예제) 

// 결제 수단이 'kakaopay'인 user_id, name, email 출력

SELECT user_id, name, email

   FROM users u

WHERE user_id in (     // user_id 중 ()에 해당되는 user_id

              select user_id FROM orders o

              where payment_method = 'kakaopay' 

)

* where절 예제2) 이씨 성을 가진 유저의 포인트의 평균보다 큰 유저들의 데이터 추출하기

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

 

SELECT * FROM point_users pu

where point > (

                         SELECT avg(point) FROM point_users pu

                         inner join users u

                         on pu.user_id = u.user_id

                         where name = '이**'

)

-----------------같은 결과(아래는 subquery 내 subquery 사용)----------------------------------------------

SELECT * FROM point_users pu

where point > (

                         SELECT avg(point) FROM point_users pu

                         where user_id in (

                                                      select user_id from users where name = '이**'

                          )

)

 

* subquery(select절) 예제) 

// '오늘의 다짐' 좋아요 수가 본인이 평소에 받았던 좋아요 수에 비해 높고 낮은지 비교 

SELECT c.checkin_id,

               c.course_id,

               c.user_id ,   //user_id가 나올 때마다 아래 서브 쿼리문을 실행 (ex)1.  4b8a 라는 user_id하나 들어왔다면 2. 그 다음 id가 fac9인 것이 들어왔다

               c.likes,

               (

                        SELECT avg(likes) FROM checkins c2

                        WHERE c2.user_id = c.user_id  //여기에 위에 c.user_id를 붙여주면서 해당 user_id당 like 개수의 평균값을 계산해 출력   => 1.  4b8a 아이디에 해당하는 평균like개수를 구함  2. fac9 아이디에 해당하는 평균like 개수 구함

                ) as avg_likes_user   // checkin_id에 대해서 해당 유저의 평균 like개수

FROM checkins c

* select절 예제2) 

// course_id별 평균 like 수 붙이기(과목명으로 출력)

SELECT c.checkin_id, c2.title, c.user_id, c.likes,

               (

                 select round(avg(likes), 1) FROM checkins

                 where course_id = c.course_i // 첫 course id 값에 c.coure id의 첫 값이 대응 이것이 계속 반복(같은 값이 들어간다는 의미)

                ) as course_avg

    FROM checkins c

inner join courses c2 on c.course_id = c2.course_id  // c2.title을 출력하기 위한 inner join

 

* subquery(from절) : 가장 많이 사용!! 예제) 

// user_id별 포인트와 좋아요의 평균 수를 파악해보고 싶다.

SELECT pu.user_id, pu.point, a.avg_likes FROM point_users pu

inner join    //subquery를 이용해 join!

                 SELECT user_idround(avg(likes), 1) as avg_likes FROM checkins c

                 group by user_id

) a on pu.user_id = a.user_id

* from절 예제2) 

// course_id 별 유저의 체크인 수와 전체 인원 그리고 비율 나타내기(과목명으로 출력)

SELECT c2.title,

               a.cnt_checkins,

               b.cnt_total,

               (a.cnt_checkins/b.cnt_total) as ratio

FROM

(

                select course_idcount(DISTINCT(user_id)) as cnt_checkins FROM checkins c // 숫자를 셀 때 중복이 없어야 하므로 count 안에 DISTINCT 를 넣었음(카운트 밖에 DISTINCT를 넣는다면 오류가 나옴 -> 밖에 넣으면 distinct가 적용이 되질 않음)

                 group by course_id

a

inner JOIN

(

                  SELECT course_id , count(*) as cnt_total FROM orders o

                  group by course_id

) b on a.course_id = b.course_id

inner join courses c2 on a.course_id = c2.course_id

=> from에 들어가는 테이블 먼저 설계 후 ()a, ()b 를 하나의 테이블이라 생각하고 전체 쿼리 작성하기

 

  • with 절 : 쿼리문을 좀 더 깔끔하게 정리하기 위해 서브쿼리를 제일 상단에 따로 정의 

* with 절 예(from절 예제 2번을 with 절을 통해 정리)

with table1 as (

                         select course_id, count(DISTINCT(user_id)) as cnt_checkins FROM checkins c

                         group by course_id

), table2 as (

                         SELECT course_id , count(*) as cnt_total FROM orders o

                         group by course_id

)

SELECT c2.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 c2 on a.course_id = c2.course_id

 

  • SUBSTRING_INDEX : 문자열을 쪼갤 때 사용(실제 업무에서는 문자열 데이터를 원하는 형태로 한 번 정리해야 하는 경우가 많음), 예제로 SUBSTRING_INDEX(email, '@', -1(뒤 숫자)) =>email을 '@'기준으로 쪼개서 뒤 숫자가 1이면 @앞 문자열 보여달라는 뜻이고 -1이면 뒷 부분 보여달라는 뜻
  • SUBSTRING : 문자열 일부만 출력 시 사용, SUBSTRING(필드명, n, m) => 해당 필드의 n에 해당하는 문자부터 m길이의 문자열을 출력(이 때 주의!! 문자열의 시작은 1이고 만약 n에 0을 넣으면 아무것도 출력이 안됨 ex) 문자열이 somemen12라면 SUBSTRING('somemen12' 1, 3) 하면 som이 출력 즉, 시작숫자가 1이다(=s))

* SUBSTRING_INDEX 예제) 

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

SELECT domain, count(*) as cnt_domain from (

                 select SUBSTRING_INDEX(email, '@', -1) as domain FROM users u

) a

group by domain

---------------------------------------같은 결과----------------------------------------------------------

SELECT SUBSTRING_INDEX(email, '@', -1) as domain , count(*) as cnt_domain

   FROM users u

group by SUBSTRING_INDEX(email, '@', -1)

 

  • CASE : 조건문이 필요할 때 사용, CASE WHEN 조건식 THEN 실행문장 ELSE 실행문장 END(WHEN 여러 개 사용 가능)

* CASE 예제) 

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

SELECT pu.point_user_id,

               pu.point,

               (case when point > (SELECT avg(point) from point_users) then '잘 하고 있어요!'

                         else '열심히 합시다!' end) as msg

FROM point_users pu

=>평균이상 포인트를 가지고 있으면 잘하고 있어요 낮으면 열심히 합시다 표시

 

* 응용 문제!!!

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

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

---------------------------------------같은 결과----------------------------------------------------------

SELECT enrolled_id,

               sum(done) as done_cnt,

               count(*) as total_cnt,

               round(sum(done)/count(*), 2) as ratio

    FROM enrolleds_detail

group by enrolled_id

=> 두개의 결과 값은 동일 하나 아래 쿼리문은 done_cnt가 0인것도 출력되므로 결과가 49개가 더 출력된다. 따라서 결과를 완벽하게 똑같이 하고 싶다면

SELECT * from (

                            SELECT enrolled_id,

                                           sum(done) as done_cnt,

                                           count(*) as total_cnt,

                                           round(sum(done)/count(*), 2) as ratio

                               FROM enrolleds_detail

                            group by enrolled_id

) a

where ratio != 0

=> 요렇게 묶으면 된다. (굳이 서브쿼리 여러 개 사용해서 복잡하게 만드는 것보다 마지막 결과처럼 간단하게 만드는 게 더 유용함 - 가끔 멀리서 보면 더 나은 쿼리 작성할 수 있다!)

 

 

- 어떤 부분은 AS명으로 사용이 되는데 또 어떤 부분은 안되는 게 헷갈린다. 한 두 번 정도 다시 보면서 익숙해져야 겠다. 서브쿼리 개념도 두 번정도 반복하다 보니 익숙해졌다.

'SQL' 카테고리의 다른 글

SQL 용어 정리  (1) 2023.05.14
부트캠프 개발일지(SQL 1, 2, 3주차)  (0) 2023.05.12