HAVING 과 집계 조건
WHERE 는 행을, HAVING 은 묶음을 거릅니다. 절이 실행되는 차례를 알면 별칭을 어디서 사용할 수 있는지도 함께 풀립니다.
묶은 뒤에 거릅니다
WHERE 는 행을 거릅니다(1.6). 그런데 2.2 처럼
묶고 나면 묶음을 조건으로 거르고 싶을 때가 있습니다. "글을 40개 이상
작성한 사람" 같은 것입니다. 그 자리가 HAVING 입니다.
SELECT U.nickname, COUNT(*) AS 글수 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num GROUP BY U.nickname HAVING COUNT(*) >= 40 ORDER BY 글수 DESC;
| nickname | 글수 |
|---|---|
| 한소망 | 44 |
| 홍길동 | 43 |
20명 가운데 둘만 남았습니다. 행이 아니라 묶음이 걸러진 것입니다.
절이 실행되는 차례
적는 차례와 실행되는 차례가 다릅니다. 이 차례 하나를 알면 이 단원의 나머지가 모두 설명됩니다.
-- 적는 차례 SELECT … FROM … JOIN … WHERE … GROUP BY … HAVING … ORDER BY … -- 실행되는 차례 FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
| 차례 | 절 | 그 시점에 있는 것 |
|---|---|---|
| 1 | FROM · JOIN | 테이블을 이어 행을 만듭니다 |
| 2 | WHERE | 행이 있습니다. 묶음은 아직 없습니다 |
| 3 | GROUP BY | 여기서 묶음이 생깁니다 |
| 4 | HAVING | 묶음이 있습니다. 집계값을 알 수 있습니다 |
| 5 | SELECT | 여기서 별칭이 붙습니다 |
| 6 | ORDER BY | 별칭이 이미 있으므로 사용할 수 있습니다 |
WHERE 에는 집계 함수를 사용할 수 없습니다
WHERE 가 실행될 때는 아직 묶음이 없습니다.
"이 사람의 글이 몇 개인지" 를 알 수 없으므로 조건으로 삼을 수도 없습니다.
SELECT U.nickname, COUNT(*) AS 글수 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num WHERE COUNT(*) >= 40 GROUP BY U.nickname;
HAVING 에는 별칭을 사용할 수 없습니다
HAVING 은 SELECT 보다
먼저 실행됩니다. 별칭은 SELECT 에서 붙으므로
그 시점에는 아직 없습니다.
SELECT U.nickname, COUNT(*) AS 글수 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num GROUP BY U.nickname HAVING 글수 >= 40;
집계식을 그대로 다시 적어야 합니다 —
HAVING COUNT(*) >= 40 입니다. 길어져도 어쩔 수 없습니다.
ORDER BY 에서는 됩니다. SELECT 가
이미 끝나 별칭이 붙어 있기 때문입니다. 1.7 에서 "별칭을 사용할 수 있는 절은
ORDER BY 뿐" 이라고 한 까닭이 이것입니다.
두 오류 모두 문장을 해석하는 단계에서 나므로 TRY…CATCH 로
잡히지 않습니다(2.2 · 4.5).
둘을 함께 사용하기
WHERE 로 어떤 행을 셈에 넣을지 정하고,
HAVING 으로 어떤 묶음을 남길지 정합니다.
SELECT U.nickname, COUNT(*) AS 자유게시판글수 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num WHERE P.board_num = 2 GROUP BY U.nickname HAVING COUNT(*) >= 20 ORDER BY 자유게시판글수 DESC, U.nickname;
| nickname | 자유게시판글수 |
|---|---|
| 강바다 | 24 |
| 박민수 | 24 |
| 정하늘 | 24 |
| 문시우 | 23 |
| 백나윤 | 23 |
| 서예린 | 23 |
| 이영희 | 23 |
| 신우진 | 22 |
자유게시판 글만 세었고, 그중 20개 이상인 사람만 남았습니다.
공지사항과 질문답변에 작성한 글은 세지 않았습니다 —
WHERE 가 묶기 전에 걸러 냈기 때문입니다.
어디에 두느냐로 질문이 달라집니다
같은 숫자를 조건에 넣어도 WHERE 에 두느냐
HAVING 에 두느냐에 따라 묻는 것이 완전히
달라집니다.
-- 조회수 400 이상인 "글"이 4개 이상인 사람 SELECT U.nickname, COUNT(*) AS 인기글수 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num WHERE P.hit_count >= 400 GROUP BY U.nickname HAVING COUNT(*) >= 4 ORDER BY U.nickname;
| nickname | 인기글수 |
|---|---|
| 김철수 | 4 |
| 박민수 | 4 |
| 이영희 | 4 |
| 임도윤 | 4 |
| 정하늘 | 4 |
| 최지우 | 4 |
| 한소망 | 4 |
| 홍길동 | 4 |
-- "작성한 글 전체의 평균" 조회수가 220 이상인 사람 SELECT U.nickname, COUNT(*) AS 글수, AVG(P.hit_count) AS 평균조회 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num GROUP BY U.nickname HAVING AVG(P.hit_count) >= 220 ORDER BY 평균조회 DESC;
| nickname | 글수 | 평균조회 |
|---|---|---|
| 이영희 | 23 | 229 |
| 오준혁 | 22 | 227 |
| 임도윤 | 24 | 221 |
앞은 잘 된 글이 몇 개인지를 묻고, 뒤는 전반적으로 잘 작성하는지를
묻습니다. 뒤엣것은 WHERE 로는 적을 수 없습니다. 평균은
묶은 뒤에야 알 수 있기 때문입니다.
먼저 걸러 낼 수 있는 조건은 WHERE 에 두십시오.
묶기 전에 행이 줄어들면 그만큼 일이 적어집니다. 집계값을 봐야만 판단할 수 있는 조건만
HAVING 에 남깁니다.
직접 해보기
댓글이 4개 이상 달린 글을 번호·제목·댓글 수와 함께 다섯 건만 가져오세요.
다음 두 질문을 각각 문장으로 적어 보세요. 조건을 어디에 두어야 하는지가 다릅니다.
- 답글(depth 가 0 이 아닌 글)을 10개 이상 작성한 사람
- 글을 20개 이상 작성한 사람 가운데 최고 조회수가 490 이상인 사람
WHERE는 행을,HAVING은 묶음을 거릅니다.- 실행 차례는 FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 입니다.
WHERE에 집계 함수를 사용할 수 없습니다. 그때는 아직 묶음이 없습니다(오류 147).HAVING에 별칭을 사용할 수 없습니다. 별칭은SELECT에서 붙습니다(오류 207).ORDER BY에서만 별칭이 됩니다. 가장 나중에 실행되기 때문입니다.- 먼저 걸러 낼 수 있는 조건은
WHERE에 두십시오. 묶을 행이 줄어듭니다.