이번 문서의 목표: 이 파일을 다 읽으면 GROUP BY로 데이터를 묶어 집계하고 HAVING으로 그룹을 걸러내는 질의를 직접 계산하며, VIEW를 만들어 재사용·보안에 활용하고, GRANT·REVOKE로 권한을 부여·회수하는 SQL을 읽고 쓸 수 있게 된다.
왜 그룹화·뷰·권한이 필요한가
13편의 조인·서브쿼리가 “여러 테이블을 하나로 합치는” 기술이었다면, 이 편은 합쳐진 데이터를 요약하고(그룹화), 자주 쓰는 질의를 재사용 가능한 형태로 저장하고(뷰), 누가 어떤 데이터를 볼 수 있는지 통제하는(권한) 기술입니다. “과목별 평균 성적이 얼마인가”, “이 사용자에게는 성적 컬럼을 숨기고 평균만 보여주고 싶다”처럼 실무와 시험 모두에서 자주 나오는 요구를 다룹니다.
쉽게 말하면: GROUP BY는 “같은 값끼리 한 덩어리로 묶기”, HAVING은 “묶은 덩어리 중 조건에 맞는 것만 남기기”, VIEW는 “자주 쓰는 질의에 이름을 붙여 테이블처럼 쓰기”, GRANT/REVOKE는 “누구에게 무엇을 허락하고 거둬들일지 정하기”입니다.
이 편은 13편과 같은 예제 스키마(STUDENT, COURSE, ENROLL)를 계속 사용합니다.
ENROLL(학번, 과목코드, 성적)
| 학번 | 과목코드 | 성적 |
|---|---|---|
| S1 | C001 | 90 |
| S1 | C002 | 85 |
| S2 | C001 | 78 |
| S3 | C001 | NULL |
| S3 | C003 | 92 |
| S4 | C002 | 88 |
| S5 | C005 | 70 |
S3, C001 행의 성적이 NULL인 것은 아직 채점이 끝나지 않은 상태를 뜻합니다. 이 행은 뒤에서 COUNT(*)와 COUNT(성적)의 차이를 보여주는 데 씁니다.
1. GROUP BY — 같은 값끼리 묶기
GROUP BY는 지정한 컬럼의 값이 같은 행들을 하나의 그룹으로 묶고, 그 그룹마다 집계 함수(COUNT, SUM, AVG, MAX, MIN)를 적용합니다.
SELECT 과목코드, AVG(성적) AS 평균성적, COUNT(*) AS 전체행수, COUNT(성적) AS 채점행수
FROM ENROLL
GROUP BY 과목코드;과목코드별로 행을 묶어 계산해 보겠습니다.
| 과목코드 | 속한 행 | 평균성적(AVG) | 전체행수(COUNT(*)) | 채점행수(COUNT(성적)) |
|---|---|---|---|---|
| C001 | (S1,90), (S2,78), (S3,NULL) | (90+78)÷2 = 84 | 3 | 2 |
| C002 | (S1,85), (S4,88) | (85+88)÷2 = 86.5 | 2 | 2 |
| C003 | (S3,92) | 92 | 1 | 1 |
| C005 | (S5,70) | 70 | 1 | 1 |
C001 그룹의 계산을 한 줄씩 짚어 봅니다.
- 분자의
90 + 78은성적이NULL이 아닌 행(S1, S2)의 값만 더한 것입니다. - 분모의
2도NULL이 아닌 행의 개수입니다. 집계 함수는COUNT(*)를 제외하면NULL을 계산에서 제외합니다.
자주 틀리는 점: COUNT(*)는 NULL 여부와 무관하게 행 자체의 개수를 세지만, COUNT(컬럼명)은 그 컬럼 값이 NULL이 아닌 행만 셉니다. C001 그룹에서 COUNT(*)는 3이지만 COUNT(성적)은 2인 이유가 여기에 있습니다. “다음 집계 함수 결과로 옳은 것은?” 유형에서 NULL 포함 여부를 놓치면 바로 틀립니다.
2. HAVING — 그룹을 거르는 조건
HAVING은 GROUP BY로 만든 그룹 단위에 조건을 거는 절입니다. 반면 WHERE는 그룹을 만들기 전에 개별 행 단위로 조건을 겁니다.
SELECT 과목코드, AVG(성적) AS 평균성적
FROM ENROLL
GROUP BY 과목코드
HAVING AVG(성적) >= 85;위에서 계산한 그룹별 평균(C001: 84, C002: 86.5, C003: 92, C005: 70) 중 85 이상인 그룹만 남기면 다음과 같습니다.
| 과목코드 | 평균성적 |
|---|---|
| C002 | 86.5 |
| C003 | 92 |
SQL이 실제로 처리되는 논리적 순서를 그림으로 보면 WHERE와 HAVING이 왜 다른 단계인지 명확해집니다.
자주 틀리는 점: WHERE 절에는 집계 함수를 직접 쓸 수 없습니다(WHERE AVG(성적) >= 85는 오류입니다). 이유는 위 흐름에서 보듯 WHERE는 그룹이 만들어지기 전 단계에서 실행되어 아직 “평균”이라는 개념 자체가 존재하지 않기 때문입니다. 집계 함수로 조건을 걸고 싶다면 반드시 HAVING을 써야 합니다.
3. VIEW — 질의를 저장해 테이블처럼 쓰기
뷰(view)는 SELECT문을 이름 붙여 저장한 가상 테이블입니다. 실제로 데이터를 다시 저장하는 것이 아니라, 뷰를 조회할 때마다 저장된 SELECT문이 다시 실행됩니다.
CREATE VIEW 과목별평균성적 AS
SELECT 과목코드, AVG(성적) AS 평균성적
FROM ENROLL
GROUP BY 과목코드;이제 이 뷰를 일반 테이블처럼 조회할 수 있습니다.
SELECT * FROM 과목별평균성적 WHERE 평균성적 >= 85;결과는 앞서 계산한 HAVING 예제와 같은 C002(86.5), C003(92)입니다. 뷰를 쓰면 좋은 점은 다음과 같습니다.
- 재사용: 복잡한 조인·그룹화 질의를 매번 다시 쓰지 않고 뷰 이름 하나로 호출한다.
- 단순화: 사용자는 내부에 몇 개 테이블이 조인되어 있는지 몰라도 뷰만 조회하면 된다.
- 보안: 원본 테이블의 일부 컬럼·행만 보여주는 뷰를 만들어, 민감한 컬럼(예: 개별 학생의 원본 성적)을 숨기고 요약값만 공개할 수 있다. 접근 통제의 자세한 원리는 23편(보안·권한)에서 더 다룬다.
자주 틀리는 점: 모든 뷰가 INSERT·UPDATE로 갱신 가능한 것은 아닙니다. 위 과목별평균성적처럼 GROUP BY·집계 함수가 들어간 뷰, 여러 테이블을 조인한 뷰, DISTINCT가 들어간 뷰는 일반적으로 갱신 불가능한 뷰(non-updatable view)입니다. “이 뷰에 UPDATE문을 실행하면?”이라는 문제에서 뷰의 정의(집계·조인 포함 여부)를 먼저 확인해야 합니다.
4. GRANT / REVOKE — 권한을 주고 거둬들이기
DCL(Data Control Language, 데이터 제어어)은 데이터베이스에 대한 접근 권한을 관리하는 SQL 명령어 집합으로, 대표적으로 GRANT(권한 부여)와 REVOKE(권한 회수)가 있습니다.
GRANT SELECT, INSERT ON ENROLL TO STU_USER;STU_USER라는 사용자에게 ENROLL 테이블에 대한 조회(SELECT)와 삽입(INSERT) 권한을 부여합니다. 뷰에도 똑같이 권한을 줄 수 있습니다.
GRANT SELECT ON 과목별평균성적 TO STU_USER WITH GRANT OPTION;WITH GRANT OPTION은 이 사용자가 받은 권한을 다른 사용자에게 다시 부여할 수 있게 허락하는 옵션입니다. 권한을 되돌릴 때는 REVOKE를 씁니다.
REVOKE INSERT ON ENROLL FROM STU_USER;ENROLL에 대한 INSERT 권한만 회수하고, 앞서 준 SELECT 권한은 그대로 유지됩니다.
Oracle에서 권한은 크게 두 종류로 나뉩니다.
| 권한 종류 | 의미 | 예시 |
|---|---|---|
| 시스템 권한(system privilege) | 데이터베이스 객체를 만들거나 세션에 접속하는 등 시스템 차원의 동작 권한 | CREATE TABLE, CREATE SESSION |
| 객체 권한(object privilege) | 특정 테이블·뷰 등 객체에 대한 조작 권한 | SELECT, INSERT, UPDATE, DELETE ON 특정 테이블 |
자주 틀리는 점: GRANT·REVOKE를 DML(Data Manipulation Language, 데이터 조작어)로 분류하는 오답 보기가 자주 나옵니다. GRANT·REVOKE는 데이터 자체가 아니라 권한을 다루므로 DCL입니다. DDL(CREATE·ALTER·DROP), DML(SELECT·INSERT·UPDATE·DELETE), DCL(GRANT·REVOKE)의 분류를 헷갈리지 않아야 합니다.
WHERE vs HAVING, DML vs DCL 비교
| 구분 | WHERE | HAVING |
|---|---|---|
| 적용 시점 | GROUP BY 이전, 행 단위 | GROUP BY 이후, 그룹 단위 |
| 집계 함수 사용 | 불가능 | 가능 |
| 목적 | 개별 행을 거른다 | 그룹(요약값)을 거른다 |
| 구분 | DML | DCL |
|---|---|---|
| 대표 명령어 | SELECT, INSERT, UPDATE, DELETE | GRANT, REVOKE |
| 다루는 대상 | 데이터 자체 | 데이터에 대한 접근 권한 |
| 실행 결과 | 데이터가 조회·변경된다 | 사용자의 권한이 변경된다 |
핵심 정리
- GROUP BY는 같은 값을 가진 행을 그룹으로 묶고, 그 안에서 집계 함수를 계산한다. COUNT(*)는 NULL 여부와 무관하게 행 수를 세지만 COUNT(컬럼)은 NULL을 제외하고 센다.
- WHERE는 그룹이 만들어지기 전 행 단위 필터이므로 집계 함수를 쓸 수 없고, HAVING은 그룹이 만들어진 후 그룹 단위로 집계 함수 조건을 건다.
- SQL의 논리적 처리 순서는 FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY이다.
- VIEW는 SELECT문을 저장한 가상 테이블로 재사용·단순화·보안에 쓰이지만, 집계·조인·DISTINCT가 포함된 뷰는 일반적으로 갱신이 불가능하다.
- DCL의 GRANT는 권한을 부여하고 REVOKE는 권한을 회수하며, Oracle 권한은 시스템 권한과 객체 권한으로 나뉜다. GRANT·REVOKE는 DML이 아니라 DCL이다.