GROUP BY
: 그룹을 묶어줄 기준을 제시할 수 있는 구문이다.
→ 그룹 함수와 함께 사용한다.
- GROUP BY 묶어줄 기준 컬럼
- 제시된 기준별로 그룹을 묶을 수 있다.
- 여러 개의 값들을 하나의 그룹으로 묶어서 처리할 목적으로 사용한다.
- 각 부서별로 총 급여 합계 조회하기
SELECT DEPT_CODE, SUM(SALARY)
FROM EMPLOYEE
GROUP BY DEPT_CODE;
우선 SELECT절에 부서코드와 SALARY(급여) 컬럼을 넣은 SUM 함수를 작성한다. 그리고 DEPT_CODE 컬럼과 SALARY 컬럼은 EMPLOYEE 테이블에 있으므로 FROM절에 EMPLOYEE를 작성한다. 마지막으로 급여 합계를 각 "부서"별로 조회해야 하므로 부서코드 기준으로 GROUP BY를 사용해 그룹을 묶어준다.
- 각 직급별로 직급코드와 총 급여의 합계, 사원 수, 보너스를 받는 사원 수, 평균 급여, 최고 급여, 최저 급여를 조회하기
SELECT JOB_CODE "직급코드", SUM(SALARY) "총 급여 합", COUNT(*) "전체 사원 수"
, COUNT(BONUS) "보너스 받는 사원 수", FLOOR(AVG(SALARY)) "평균 급여", MAX(SALARY) "최고 급여"
, MIN(SALARY) "최저 급여"
FROM EMPLOYEE
GROUP BY JOB_CODE
문제에서 각 "직급"별로 조회하라 했으므로 GROUP BY를 사용하여 JOB_CODE를 기준으로 그룹을 묶으면 된다.
HAVING
: 그룹에 대한 조건을 제시할 때 사용한다.
→ 주로 그룹 함수와 함께 조건을 제시한다.
- GROUP BY절 뒤에 쓴다.
- 각 부서별로 평균 급여를 구했을 때 평균 급여가 300만 원 이상인 부서들만 조회하기
SELECT DEPT_CODE "부서코드", FLOOR(AVG(SALARY)) "평균 급여"
FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING FLOOR(AVG(SALARY)) >= 3000000;
문제에서 각 부서별로 조회하라 했으므로 DEPT_CODE를 기준으로 그룹을 묶으면 된다. 그리고 평균 급여가 300만 원 이상이라 했는데, 평균 급여는 그룹 함수이므로 HAVING절을 사용하여 위와 같이 조건을 제시하면 된다. 이때 주의할 점은 GROUP BY로 그룹을 하나로 묶은 다음에 HAVING절로 그 그룹의 조건을 제시해야 한다. 만약 HAVING절을 GROUP BY절보다 앞서 작성하면 구문 오류가 발생하게 되므로 순서를 꼭 지켜서 작성하는 것이 좋다.
SET OPERATOR
: 여러 개의 쿼리문을 하나의 쿼리문으로 만드는 집합 연산자이다.
- 주의 : 두 쿼리문의 결과를 합쳐서 하나의 테이블로 나타내야 하므로 두 쿼리문의 SELECT절은 일치해야 한다.
(1) UNION
: 두 쿼리문을 수행한 결과값을 더한 후 중복되는 부분은 제거한 후 반환한다.(합집합, OR)
- 부서코드가 D5이거나 급여가 300만 원 초과인 직원들의 직번, 이름, 부서코드, 급여를 조회하기
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'
UNION
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000;
선행 쿼리문은 부서코드가 D5일 때 해당 부서 직원들의 정보를 조회하는 것이고, 후행 쿼리문은 급여가 300만 원 초과인 해당 직원들의 정보를 조회하는 것이다.
왼쪽 출력창을 보면 부서코드가 D5이면서 급여가 300만 원 초과인 김형팔이 있다. 김형팔은 부서코드가 D5이므로 선행 쿼리문에서 한 번 출력되고, 급여가 350만 원이므로 후행 쿼리문에서 한 번 더 출력되어 총 두 번 출력되어야 한다. 그런데 한 번만 출력되었다. 그 이유는 선행 쿼리문과 후행 쿼리문 사이에 중복값을 제거해주는 UNION이 있기 때문이다.
그래서 UNION은 합집합이라고 보면 된다.
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' OR SALARY > 3000000;
합집합은 A 집합과 B 집합에서 두 집합의 교집합을 뺀 것으로 'x|x∈A 또는 x∈B' 라고 표현한다. 여기서 보다시피 '또는'은 OR이므로 UNION은 OR 연산자와 같은 의미라는 것을 알 수 있다.
위는 UNION을 이용한 쿼리문을 OR 연산자로 바꿔 본 것이다. UNION은 SELECT절부터 FROM절까지 구문이 중복되므로 UNION보단 OR 연산자를 사용하는 것이 더 나아보인다.
(2) UNION ALL
: 두 쿼리문을 수행한 결과값을 더한 후 중복 제거를 하지 않고 반환한다..(합집합 + 교집합)
- 직급코드가 J6이거나 부서코드가 D1인 직원들의 주민등록번호, 이름, 부서코드, 직급코드를 조회하기(단, 중복값을 제거하지 않고 조회한다.)
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE JOB_CODE = 'J6'
UNION ALL
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';
문제에서 중복값을 제거하지 않고 조회한다 했으므로
UNION ALL을 사용하여 중복값도 반환한다.
왼쪽 출력창을 보면 전다연은 직급코드가 J6이므로 선행 쿼리문 조건을 만족하여 한 번 출력되고, 부서코드도 D1이어서 후행 쿼리문 조건도 만족하여 한 번 더 출력되었다.
(3) INTERSECT
: 두 쿼리문을 수행한 결과값의 중복된 부분만을 반환한다.(교집합, AND)
- 직급코드가 J6이면서 부서코드가 D1인 직원들을 조회하기
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE JOB_CODE = 'J6'
INTERSECT
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';
선행 쿼리문의 조건은 직급코드가 J6이어야 한다는 것이고, 후행 쿼리문의 조건은 부서코드가 D1이어야 한다는 것이다. 문제를 보면 직급코드가 J6"이면서" 부서코드가 D1인 직원들을 조회하는 것이므로 선행 쿼리문의 조건과 후행 쿼리문의 조건 모두를 만족시켜야 한다. 이때 INTERSECT를 사용하여 두 쿼리문을 교집합으로 만들어주면 모든 조건을 만족시키는 결과값이 나온다.
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE JOB_CODE = 'J6' AND DEPT_CODE = 'D1';
위의 INTERSECT를 사용한 쿼리문은 조건식의 모든 조건을 만족시키는 AND 연산자로 짧게 줄일 수 있다.
(4) MINUS
: 선행 쿼리문의 결과값에서 후행 쿼리문의 결과값을 뺀 나머지 부분을 반환한다.(차집합)
- 직급코드가 J6인 직원들 중에서 부서코드가 D1인 직원들을 제외한 나머지 직원들을 조회하기
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE JOB_CODE = 'J6'
MINUS
SELECT EMP_NO, EMP_NAME, DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';
선행 쿼리문은 직급코드가 J6에 해당하는 직원들의 정보를 조회하고, 후행 쿼리문은 부서코드가 D1에 해당하는 직원들의 정보를 조회한다.
그런데 문제에서 직급코드가 J6인 직원들 중 부서코드가 D1인 직원들을 제외한 나머지 직원들을 조회하라고 했으므로, 선행 쿼리문에서 후행 쿼리문과 중복되는 값들을, 하나만 남기는 것이 아니라, 아예 싹 다 제거한 후 출력하면 된다.
즉, 직급코드가 J6인 직원들 중에서 부서코드가 D1 외에도 다른 부서코드들이 있을 건데 거기서 D1만 뺀 다른 부서코드의 직원들을 뽑아내면 된다는 말이다.