점점 코드카타 하기가 두렵다
안배웠던 구문을 찾아서 써보기도 하는데..
조급하게 생각하지 말고, 습득한거 써먹어보며 천천히 풀어보자!
- MEMBER_PROFILE와 REST_REVIEW 테이블에서 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회하는 SQL문을 작성해주세요. 회원 이름, 리뷰 텍스트, 리뷰 작성일이 출력되도록 작성해주시고, 결과는 리뷰 작성일을 기준으로 오름차순, 리뷰 작성일이 같다면 리뷰 텍스트를 기준으로 오름차순 정렬해주세요.
문제를 보다 보니 이것 역시 HAVING이 필요한 구문같다.
더이상 미루지 말고 HAVING에 대해 정리하고 가자
SELECT [DISTINCT|ALL] 컬럼 OR 그룹함수
FROM 테이블
WHERE 조건
GROUP BY 그룹대상
HAVING <그룹함수 포함조건>
ORDER BY 정렬대상HAVING : 조건중에 그룹 함수를 포함하는 조건
- 그룹을 필터링하는 2차 필터링
- ORACLE에서는 HAVING절과 GROUP BY순서가 정해져있지 않지만 다른 DBMS에서는 GROUP BY가 HAVING절보다 앞에 작성되어야 한다.
- 논리적으로도 HAVING은 그룹된 결과에 대한 조건이므로 GROUP BY 절 뒤에 기술하는 것이 좋다.
- GROUP BY 절에 작성되지 않은 일반컬럼은 HAVING절에 올 수 없다.
그룹함수(집계함수)?
검색된 여러 행을 이용해 통계 정보를 계산하는 함수
COUNT, SUM, AVERAGE, MIN, MAX, STDDEV(표준편차), VARIANCE(분산) 등이
그룹함수 고려사항!
- NULL값은 무시된다.(값이 NULL은 제외하고 수행함)
- 반드시 단 하나의 값만 반환한다.
- GROUP BY열을 SELECT 절에 포함시키지 않아도 된다.
- 하나 이상의 GROUP BY 열을 나열하여 그룹에 대한 요약 결과를 조회할 수 있다.
- SELECT 절의 일반 컬럼과 GROUP BY 절의 일반 컬럼은 개수가 동일해야 한다.
예제
1. 부서별(DNO) 급여(SAL) 평균이 3천 달러 미만인 부서의 부서번호(DNO)와 평균 급여 검색
SELECT DNO,
AVG(SAL)
FROM EMP
GROUP BY DNO
HAVING AVG(DNO) < 3000
2.기말고사 성적이 핵 화학과목보다 우수한 과목의 과목명과 담당 교수명을 검색
SELECT A.CNO
,B.CNAME
,C.PNAME
,ROUND(AVG(A.RESULT),2) 기말고사_성적_평균
FROM SCORE A, COURSE B, PROFESSOR C
WHERE A.CNO = B.CNO
AND B.PNO = C.PNO
GROUP BY A.CNO, B.CNAME, C.PNAME
HAVING AVG(RESULT) > (SELECT AVG(RESULT)
FROM SCORE A, COURSE B
WHERE A.CNO = B.CNO
AND B.CNAME = '핵화학')
ORDER BY A.CNO;
HAVING을 사용한 문제풀이(답안 따옴)
-- HAVING 사용해보기
-- 1. JOIN으로 묶기
-- 2. WHERE절에 MEMBER_ID 추출
-- 3. HAVING으로 그룹함수 COUNT로 최대값 모두 추출
SELECT
M.MEMBER_NAME,
R.REVIEW_TEXT,
DATE_FORMAT(R.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
FROM MEMBER_PROFILE M INNER JOIN REST_REVIEW R ON M.MEMBER_ID = R.MEMBER_ID
WHERE R.MEMBER_ID IN
(
SELECT MEMBER_ID
FROM REST_REVIEW
GROUP BY MEMBER_ID
HAVING COUNT(*) =
(
SELECT COUNT(MEMBER_ID)
FROM REST_REVIEW
GROUP BY MEMBER_ID
ORDER BY COUNT(MEMBER_ID) DESC
LIMIT 1
)
)
ORDER BY 3, 2
너무 어려운 것 같고, 내가 알고 있는 범위에서도 아래와 같이 구문을 쓸 수 있었다.
where절 안에 조건을 주는 연습이 더 필요할 것 같다.
-- 1. 두개 테이블 JOIN
-- 2. 가장 많은 리뷰 회원 > WHERE절에 GROUP BY로 묶고 COUNT로 내림정렬하여 LIMIT 1
-- 3. 정렬하기
--4. 가장 많은 리뷰를 남긴 사람의 멤버 아이디 조회
SELECT
M.MEMBER_NAME,
R.REVIEW_TEXT,
DATE_FORMAT(R.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
FROM MEMBER_PROFILE M INNER JOIN REST_REVIEW R ON M.MEMBER_ID = R.MEMBER_ID
WHERE M.MEMBER_ID =
(SELECT MEMBER_ID
FROM REST_REVIEW
GROUP BY 1
ORDER BY COUNT(*) DESC
LIMIT 1)
ORDER BY 3, 2
잉 근데 둘다 정답인데, 실행결과 값이 다르다.
HAVING절은

그냥 최대값만 뽑을 때는

문제 의도상 리뷰를 가장 많이 쓴 사람을 모두 출력하는 것이 맞는 것 같다. HAVING을 이용해 최대 count를 모두 뽑아내야겠다.
두 구문의 차이에 대해 튜터님께 헬프한 내용은 아래
# SELECT
# M.MEMBER_NAME,
# R.REVIEW_TEXT,
# DATE_FORMAT(R.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
# FROM MEMBER_PROFILE M INNER JOIN REST_REVIEW R ON M.MEMBER_ID = R.MEMBER_ID
# WHERE R.MEMBER_ID IN
# (
# 멤버 아이디를 조회해서
# SELECT MEMBER_ID
# FROM REST_REVIEW
# GROUP BY MEMBER_ID
# HAVING COUNT(*) =
# (
# # 리뷰를 가장 많이쓴사람의 리뷰의 갯수(3)
# SELECT COUNT(MEMBER_ID)
# FROM REST_REVIEW
# GROUP BY MEMBER_ID
# ORDER BY COUNT(MEMBER_ID) DESC
# LIMIT 1
# )
# )
# ORDER BY 3, 2
-> 리뷰를 가장 많이 쓴 사람을 모두 뽑아내고, 멤버 아이디를 조회해서 출력
# SELECT
# M.MEMBER_NAME,
# R.REVIEW_TEXT,
# DATE_FORMAT(R.REVIEW_DATE, ‘%Y-%m-%d’) REVIEW_DATE
# FROM MEMBER_PROFILE M INNER JOIN REST_REVIEW R ON M.MEMBER_ID = # R.MEMBER_ID
# WHERE M.MEMBER_ID =
# (SELECT MEMBER_ID
# FROM REST_REVIEW
# GROUP BY 1
# ORDER BY COUNT(*) DESC
# LIMIT 1)
# ORDER BY 3, 2
-> 리뷰를 가장 많이 쓴 사람 1명(멤버아이디 정렬에 따른 1명)만 뽑아내고 조회