본문 바로가기
SQL

[SQL] SELECT 문 집계 – GROUP BY, 집계 함수, HAVING, ORDER BY 활용

by nemonemonemo 2025. 8. 12.

목적

  • 보려는 항목(들)에 대한 테이블 상의 값 자체로 보는 것이 아니라 뭔가 처리를 해서 보려고 할 때

  • 주로 집계 처리믈 하게 되는 경우가 많이 있다.
    • 어떤 것을 기준으로 묶어서 할 것인가 ! group by
      • 여러개의 값을 대표화(평균, 중앙값, 맨 앞, 총합, 카운팅 etc)
        • avg(), min(), max(),stdev() etc : 집계함수
        • count(), count (distinct ~ ) : 카운팅
    • ***where/ having 공통점 : 데이터를 중심으로 필터링
      • where : 원본 데이터 값을 중심으로 필터링
      • having : 내가 group by 새롭게 생성한 집계/생성한 값들로 필터링
      • ⇒ having/where 명확하게 구별해야함

  • Q) 구매 관련된 정보들 중에서 buytbl → 고객별로 우리 회사의 구매수량(총합)을 기준으로: 고객등급!!
# 다시 원래 sqldb 동네로 가겠다.
use sqldb;

select * from buytbl;
# --> buytbl 데이터/레코드 기준 : 1개 종류 팔릴 때 마다 찍힌 pos 로그 
# --> 이 매출 데이터를 고객 기준으로 묶어서 보자!
#     : 원본테이블 그대로 보는 것이 아니라 나의 목적으로 재형성
 
select * from buytbl
group by userID; -- 12건의 레코드가 5건으로 레코드 변형 
# ==> 결과: 고객별로 엄청 많이 구매한 이력이 있다고 해도, 이 쿼리 결과는 처음 구매한 상품(buytbl pk 기준 첫번째)의 정보만 나오게 된다 
# + 이 중에서 필요한 정보 -> userID, amount, : 기준을 단순 구매 수량으로 기준
 
select userID, amount from buytbl
group by userID;
# --> 이 결과의 애매한 점 : userID OK, amount --> 누적이 아니라 1번 구매이력
 
select userID, sum(amount) from buytbl
group by userID;

select userID, sum(amount) `총 구매건수` from buytbl
group by userID;

  • Q) 고객들의 1건의 구매가 발생할 때 (1번 buytbl 테이블에 레코드가 생성) ==> 구매 수량의 평균 계산!
select * from buytbl;
# --> amount 여러 값을 1개의 대표값 (avg) 
select avg(amount) from buytbl;
# ==> 주의!! 집계함수가 꼭 group by가 있어야만 사용되는 것은 아님 
#           주로 같이 사용되는 경우가 많은 것이지 필수는 아님 
select avg(amount) `평균구매수량` from buytbl;

  • Q) 회원id, 그 사람이 구매한 수량의 정보만 보고싶다.
select userID, amount from buytbl;

select userID, amount from buytbl
group by userID;

# ---> amount : 전체가 아니라 userID별로 처음의 amount값만 보임
#      + userID 별로 묶어서 진행...
#      + userID에 대해서 정렬(a-z)
select userID, sum(amount) from buytbl
group by userID;

select userID, sum(amount) from buytbl
group by userID
order by userID asc;

select userID, sum(amount) from buytbl
group by userID
order by userID desc;

# ==> 우량 고객 중심으로 본다고 하면 ? 정렬순서/기준 이 안 맞는다. 
  • Q) 정리된 데이터는 맞다
    • 보고싶은/ 정렬하는 기준은 userID의 a-z 사전 순이 아니라
    • 누적구매 수량 중심으로 내림차순이 보고싶다.
select userID, sum(amount) 
from buytbl
group by userID
order by sum(amount) desc;
# ---> 불편함 => sum(amout) 굳이 반복 

select userID, sum(amount) `총 구매건수`
from buytbl
group by userID
order by sum(amount) desc;

# 사람들이 자주 많이 사용하는 스타일 pandas --> pd 이런 느낌...?
# ==> 숫자를 사용해서 간접 지칭하는 방식이 있다. 
select userID `사용자ID`, sum(amount) `총 구매건수`
from buytbl
group by userID
order by `사용자ID` desc; -- 내가 만든 별칭으로 정렬 기준 언급해도 됨

select userID `사용자ID`, sum(amount) `총 구매건수`
from buytbl
group by userID
order by 2 desc;
# **2 : 내가 select 뒤에 나열한 항목들을 숫자로 언급

select userID `사용자ID`, sum(amount) `총 구매건수`
from buytbl
group by userID
order by 1 desc;
#==> 쿼리 스타일을 파악하셔야 하기 때문에 보셔야 하는 것임alter

# 참고) 기준을 작성할 때 : 내가 보고자 하는 것들을 나열한 그 순서 기준
#       => 항목을 직접 언급, 그것에 대한 별칭, 순서를 언급(1~)

  • Q) 회원id별로, 총 소비금액 확인해보자 → 이 자료를 기반으로 우리 회사에 많은 금액을 지출하신 분들을 중심으로 정렬 보기 위해 (내림차순)
select * from buytbl;

select userID, price, amount from buytbl; -- 개별 구매 건수 중심

select userID, sum(price*amount) 
from buytbl
group by userID; 
#
select userID, sum(price*amount) `총 구매금액`
from buytbl
group by userID
order by `총 구매금액` desc; 
#
select userID, sum(price*amount) `총 구매금액`
from buytbl
group by userID
order by 2 desc; 

  • Q) 1번 구매를 할 떄, 누적으로 제일 많은 수량(amount)을 사신 고객들 중에서 3명을 선정해서 (기준 : 많은 수 우선), 감사쿠폰/감사메시지 이벤트!
select * from buytbl;
#
select userID, sum(amount) from buytbl
group by userID
order by 2 desc;
#
select userID, sum(amount) from buytbl
group by userID
order by 2 desc
limit 3;
# --> 이 3분에게 문자발송을 하려고 하는데 어떻게?
#     전화번호가 없다-> 어디에 있나 -> buytbl 
#     전화번호는 usertbl에 존재함
#     +++ 다른 테이블에 내용을 가져와야할 때 -> join : 은근슬쩍 usertbl 전환

  • 참고) 잡다한 함수 몇개를 보도록 하겠다. --> 주의가 필요함
  • Q) 회원 정보 테이블에서, 가장 키가 큰 회원의 이름과 키를 출력하자
select * from usertbl;
#
select name, height from usertbl;
#
select name,  max(height) from usertbl;
# --> sql입장에서 언어변환 : usertbl에서 name, 키 본다 
#                        키 중에서 최대값만 본다 -> 1개만 보여줌 
#                        name은 작성자가 언급 안 해서 그냥 처음 것을 보여줌 (이름따로 키따로 출력된 것) 
# ==> 의도 : name 중에서는 제일 처음 이름을 보고싶다 
#           키는 제일 큰 값을 보고싶다 
#         : 독립적인 정보 2개를 보고싶어서 요청/ 나열 
#name과 max(height)를 연결된 정보로 처리하고 싶다면 

# sol1) 서브 쿼리 사용해서 연결
select name, height 
from usertbl 
where height = 최대키;
#
select name, height 
from usertbl 
where height = (select max(height) from usertbl);

#sol2) group by
select name, max(height)
from usertbl
group by name
order by 2 desc
limit 1;
# ==> 2가지 쿼리가 머리 속에서 그림이 그려져야함 
# ++ 쿼리마다 속도의 차이도 발생을 함 


  • 카운팅
  • Q) 고객들 중에서 핸드폰 정보를 입력한 회원은 몇 명?
# --> counting 숫자를 세자 
select * from usertbl;

# ---> 전체 이 테이블에 레코드/데이터 수는 몇 개?
select count(*) from usertbl;

# --> 사용할 때 주의해야하는 쿼리 : 테이블이 엄청 크면, 버벅거림이 심한 코드
#     count(*) 주의해서 사용할 쿼리 
# --> 대안은? 

# 대안1) PK나 특정 컬럼(not null인)을 지정하기 
select count(userID) from usertbl; -- 10개
#
select count(name) from usertbl; -- 10개 
#
select count(mobile1) from usertbl; -- 에러 없지만 숫자 8개...
# ==> 전체 테이블의 레코드 수 : null이 없는 컬럼/필드명 중심으로 해야함 

# 대안2) +1 카운팅 누적
select count(1) from usertbl;
# ===> 구분적으로 카운팅 해야하는 경우들이 있다. 
#      업그레이드 : sum ~~ case when ~~~ : 상황별 집계/카운팅 

# --> 좀 데이터가 있는 테이블의 레코드를 체크 해보겠다.
select count(*) from employees.employees; -- 약 30만건 300024, 14s
select count(1) from employees.employees; -- 약 30만건 300024,14s
select count(emp_no) from employees.employees; -- 30만건 300024, 14s
# 주의 ! --> count(특정 컬럼) : null의 유뮤에 따라서 결과 숫자가 다를 수 있음 => 속편하게 count(1) 써라 

  • ***having vs where
    • where : 기존 테이블에 있는 레코드 값 기준으로 필터링
    • having : 내가 새롭게 만든 값을 기준으로 필터링
  • Q) 고객별로, 구매 금액이 총합을 기준으로 내림차순 정렬!
select userID, sum(price*amount) 
from buytbl
group by userID;
#
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
order by 2 desc;
# ---> Q) 위의 결과/고객별로 총누적금액의 기준으로 1000이상인 고객들에 한해서 ~~~~
#        : 필터링 기준 --> 테이블 상에 원본 값이 아니라, 
#                         그걸로 1차 가공한 누적 금액이 기준!
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
having sum(price*amount) > 1000;
#
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
where sum(price*amount) > 1000; -- 에러남 
#
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
having sum(price*amount) > 1000
order by sum(price*amount) asc;
#
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
having 2 > 1000 -- 이건 또 에러남 2>1000?:?? 말이 안 되는 수식 발생 
order by sum(price*amount) asc;
#
select userID, sum(price*amount) `누적금액`
from buytbl
group by userID
having sum(price*amount) > 1000
order by 2 asc;

# 참고) 무조건 숫자로 변경이 능사가 아니다 --> 2>1000 같은 경우가 될 수도... 보면서 쓰자 

정리

  • select 쿼리를 작성하는 순서에 대한 대략적인 그림
    select 볼 대상(들), 집계, ...(여러개일 때는 독립인지 종속인지도 고려해야함)
    from 출처 ~~~~
    where 기존 값을 기준으로 필터링(between A and B, in (), 서브쿼리) etc
    group by 기준 ~ 
    having 가공값을 중심으로 필터링 
    order by ~~~~~~
    limit ~~~~
    ​