혼자 공부하는 SQL chapter 03-2 좀 더 깊게 알아보는 SELECT 문

손지호·2024년 7월 14일
0

혼자 공부하는 SQL

목록 보기
11/14

SELECT 문에서는 결과의 정렬을 위한 ORDER BY, 결과의 개수를 제한하는 LIMIT, 중복된 데이 터를 제거하는 DISTINCT 등을 사용할 수 있다.

그리고 GROUP BY 절은 지정한 열의 데이터들을 같은 데이터끼리는 묶어서 결과를 추출한다. 주로 그룹으로 묶는 경우는 합계, 평균, 개수 등을 처리할 때 사용하므로 집계 함수와 함께 사용된다. GROUP BY 절에서도 HAVING 절을 통해 조건식을 추가할 수 있다. HAVING 절은 WHERE 절과 비슷해 보이지만, GROUP BY 절과 함께 사용되는 것이 차이점이다.


ORDER BY 절

SELECT 절의 형식은 다음과 같다. 이 중에서 아직 다루지 않은 ORDER BY와 LIMIT에 대해 서 먼저 살펴보고 잠시 후에 GROUP BY, HAVING에 대해서 알아보자.

SELECT 열_이름
   FROM 테이블_이름
   WHERE 조건식
   GROUP BY 열_이름
   HAVING 조건식
   ORDER BY 열_이름
   LIMIT 숫자

ORDER BY 절은 결과의 값이나 개수에 대해서는 영향을 미치지 않지만, 결과가 출력되는 순서를 조절한다. 데뷔 일자(debut_date)가 빠른 순서대로 출력되었습니다.

SELECT mem_id, mem_name, debut_date
   FROM member
   ORDER BY debut_date;

데뷔 일자(debut_date)가 늦은 순서대로 정렬하려면 어떻게 해야할까? 간단히 제일 뒤에 DESC라고 붙여주면된다. 기본값은 ASC인데 Ascending의 약자로 오름차순을 의미하고, DESC는 Descending의 약자로 내림차순을 의미한다.

SELECT mem_id, mem_name, debut_date
  FROM member
  ORDER BY debut_date DESC;

ORDER BY절과 WHERE 절은 함께 사용할 수 있습니다. 평균 키(height)가 164 이상인 증류 키가 큰 순서대로 조회해보자. 그런데 오류가 발생함. 이유는 SQL 구문의 순서가 틀렸기 때문!

SELECT mem_id, mem_name, debut_date, height
  FROM member
  ORDER BY height DESC
  WHERE height >= 164;   → 오류 발생

ORDER BY 절은 WHERE 다음에 나와야한다. 다음과 같이 수정하고 다시 실행해보자. 평균 키 (height)가 164 이상인 회원들이 키가 큰 순서대로 출력되었다.

SELECT mem_id, mem_name, debut_date, height
  FROM member
  WHERE height >= 164
  ORDER BY height DESC;

하지만 한 가지 더 고려할 사항이 있다. 잇지와 트와이스의 평균키(height)는 167로 동일한데 잇지가 먼저 출력되었음. 트와이스 입장에서는 자신들이 데뷔 일자(debut_date)가 더 빠르므로 먼저 나와야 된다고 생각할 수 있다.
정렬 기준은 1개 열이 아니라 여러 개 열로 지정할 수 있다. 우선 첫 번째 지정 열로 정렬한 후에 동일할 경우에는 다음 지정 열로 정렬할 수 있다. 즉, 평균 키가 큰 순서대로 정렬하되, 평균 키 가 같으면 데뷔 일자가 빠른 순서로 정렬한다.

SELECT mem_id, mem_name, debut_date, height
  FROM member
  WHERE height >= 164
  ORDER BY height DESC, debut date ASC;

#### 출력의 개수를 제한: LIMIT **LIMIT**는 출력하는 개수를 제한한다. 예를 들어, 회원 테이블(member)을 조회하는데 전체 중 앞에서 3건만 조회할 수 있다. ``` SELECT * FROM member LIMIT 3: ``` 결과에는 문제가 없지만, 이렇게 아무런 기준 없이 앞에서 3건만 뽑는 경우는 별로 없다. 먼저 정렬한 후 앞에서 몇 건을 추출하는 것이 대부분. 예를 들어, 데뷔 일자(debut_date)가 빠른 회원 3건만 추출하려면 다음과 같이 ORDER BY와 함께 사용 가능.

LIMIT 형식은 LIMIT 시작, 개수이다. 지금과 같이 LIMIT 3만 쓰면 LIMIT 0, 3과 동일합니다. 즉, 0번째부터 3건이라는 의미.

SELECT men name, debut date
  FROM member
  ORDER BY debut date
  LIMIT 3;

필요하다면 중간부터 출력도 가능. 다음과 같이 평균키(height)가 큰 순으로 정렬하되, 3번 째부터 2건만 조회할 수 있다.

SELECT menti nane, height
  FROM member
  ORDER BY height DESC
  LIMIT 3, 2;

LIMIT 시작, 개수는 LIMIT 개수 OFFSET 시작이라고 쓰는 것과 동일. 또한 UMIT는 첫 데이터를 0번으로 설정하고 시작.

중복된 결과를 제거: DISTINCT

DISTINCT는 조회된 결과에서 중복된 데이터를 1개만 남김. 여기서는 회원들의 지역(addr)을 출력해보자.

다음 SQL의 결과를 보면 회원이 사는 지역(addr)은 경기, 경남, 서울, 전남, 경북 등 5군데. 지금은 데이터 건수가 적은데도 중복된 것을 눈으로 골라내기가 어렵.

SELECT addr FROM member;

그럼 앞에서 배운 ORDER BY 사용. 같은 지역(addr)이 몰려 있어서 아까보다는 세기가 쉽지만 이 역시 데이터 건수가 수만 개라면 현실적을 종류 세는 것은 너무 어려움.

SELECT addr FROM ORDER BY addr;

이를 간단하게 하는 것이 DISTINCT 문. 열 이름 앞에 DISTINCT를 써주기만 하면 중복된 데이터를 1개만 남기고 제거.

SELECT DISTINCT addr FROM member;

GROUP BY 절은 말 그대로 그룹으로 묶어주는 역할. 103쪽 '인터넷 마켓 DB 구성도'를 살펴보면, 다음 SQL을 사용하면 market_db의 구매 테이블(buy)에서 회원(mem_id)이 구매 한 물품의 개수(amount)를 구할 수 있다.

회원(mem_id)별로 여러 건의 물건 구매가 있었고, 각각의 행이 별도로 출력됨. APN(에 이핑크) 회원의 경우에는 1+2+1+1=5개의 물건을 구매. 그런데 합계를 이렇게 암산이나 계산기로 계산해야 한다면 MySQL을 사용할 이유가 없을 것.

SELECT mem_id, amount FROM buy ORDER BY mem_id;

이럴 때 집계 함수 사용하면 됨. 집계 함수는 주로 GROUP BY 절과 함께 쓰이며 데이터를 그룹화(grouping)해주는 기능함.

집계 함수

GROUP BY와 함께 주로 사용되는 집계 함수는 다음 표와 같습니다.

  • SUM() : 합계 구함
  • AVG() : 평균 구함
  • MIN() : 최솟값 구함
  • MAX() : 최댓값 구함
  • COUNT() : 행의 개수 셈
  • COUNT(DISTINCT()) : 행의 개수 셈(중복은 1개만 인정)

각 회원(mem_id)별로 구매한 개수(amount)를 합쳐서 출력하기 위해서는 집계 함수인 SUM() 과 GROUP BY 질을 사용하면 된다. 즉, GROUP BY로 회원별로 묶어준 후에 SUM() 함수로 구매한 개수를 합치면 됨.

SELECT men_id, SUN(amount) FROM buy GROUP BY men_id;

일에서 배운 범칭을 사용해서 다음과 같이 결과를 보기 좋게 만들 수 있다.

SELECT mem_id "회원 아이디", SUM(amount) "총 구매개수" 
FROM buy GROUP BY men_id;

별칭에 작은따옴표를 사용해도 되지만 작은마옴표는 INSERT 등에서 문자를 사용하므로 별칭에는 큰 따옴표를 사용할 것을 권장.

이번에는 회원이 구매한 금액의 총합을 출력. 구매한 금액은 가격(price) * 수량 (amount)이다. 역시 합계는 SUM()을 사용하면 됩니다.

SELECT dem_id "회원 아이디", SUM(price*amount) "총 구매 금액" 
FROM buy GROUP BY mem_id;

전체 회원이 구매한 물품 개수(amount)의 평균을 구해보자. 즉, 회원이 한 번 구매할 때마 다 평균 몇 개를 구매하는지 알아보는 것. 평균 구에 개수의 결과는 3.0개입니다.

SELECT AVG(amount) "평균 구매 개수" FROM buy;

이번에는 각 회원이 한 번구매 시 평균 몇 개를 구매했는지 알아보자. 회원(mem_id)별로 구해야 하므로 GROUP BY를 사용하면 됨.

SELECT mem_id, AVG(amount) "평군 구매 개수" 
FROM buy
GROUP BY mem_id;

이번에는 회원 테이블(member)에서 연락처가 있는 회원의 수를 카운트 해보자. 그런데 결과는 전체 회원 수인 10명이 나온다.

SELECT COUNT(*) FROM member;

연락처가 있는 회원만 카운트하려면 국번(phone1) 또는 전화번호(phone2)의 열 이름을 지정해야 한다. 그러면 NULL 값인 항목은 제외하고 카운트하여 결국 연락처가 있는 회원의 인원만 나온다. 예상한 대로 8명이 나왔다.

SELECT COUNT(phone1) "연락처가 있는 희원" FROM member;

COUNT()는 모든 개수를 세고, COUNT(열이름) 열 이름의 값이 NULL인 것을 제외한 행의 개수를 센다._

Having 절

앞에서 살펴보았던 SUM()으로 회원(mem_id)별 총 구매액을 구해보자.

SELECT mem_id"회원 아이디", SUM(price*amount) "총 구매 금액" 
FROM buy 
GROUP BY mem_id;

결과 중에서 총 구매액이 1000 이상인 회원에게만 사은품을 증정하려면 어떻게 해야 할까? 아마도 조건을 포함하는 WHERE 절을 생각했을 것이다. 그런데 다음과 같이 실행해보니 오류가 발생한다. 오류 메시지를 보면 집계 함수는 WHERE 절에 나타날 수 없다는 내용이다.

SELECT mem_id"회원 아이디", SUM(price amount) "총 구매 금액" 
  FROM buy 
  WHERE SUM(price amount) > 1000; 
  GROUP BY mem id;

[오류 메시지
Error Code: 1111. Invalid use of group function]

이럴 때 WHERE 대신에 사용되는 것이 HAVING 절. HAVING은 WHERE와 비슷한 개념으로 조건을 제한하는 것이지만, 집계 함수에 대해서 조건을 제한하는 것이라고 생각하면 된다. 그리고 HAVING 절은 꼭 GROUP BY 절 다음에 나와야 한다.

SELECT mem_id "회원 아이디", SUM(price*amount) "충 구매 금액"
  FROM buy
  GROUP BY mem id
  HAVING SUM(price amount) > 1000;

GROUP BY와 관련된 조건은 HAVING을 사용해야 한다.

만약 총 구매액이 큰 사용자부터 나타내려면 ORDER BY를 사용하면 됩니다.

SELECT men id "회원 아이디", SUM(price*amount) "총 구매 금액"
  FROM buy
  GROUP BY mem_id 
  HAVING SUM(price amount) > 1000 
  ORDER BY SUR(price amount) DESC;

이로써 SELECT 문과 관련된 기본적인 SQL 형식은 모두 살펴봤다.


정리

  • ORDER BY는 결과가 출력되는 순서를 조절한다. 오름차순인 ASC와 내림차순인 DESC 중 선택 가능.
  • LIMIT는 출력하는 개수를 제한하며, 주로 ORDER BY와 함께 사용.
  • DISTINCT는 조회된 결과에서 중복된 것은 1개만 남기며, 열 이름 앞에 붙여주면 됨.
  • GROUP BY는 데이터를 그룹으로 묶어주는 기능을 한다.
  • HAVING은 집계 함수와 관련된 조건을 제한하며, GROUP BY 다음에 나옴.

관련 중요 용어

집계 함수

  • SUM() : 합계 구함
  • AVG() : 평균 구함
  • MIN() : 최솟값 구함
  • MAX() : 최댓값 구함
  • COUNT() : 행의 개수 셈
  • COUNT(DISTINCT()) : 행의 개수 셈(중복은 1개만 인정)
profile
초보 중의 초보. 열심히 하고자 하는 햄스터!

0개의 댓글