4일차 교육 핵심내용 정리
1. SELECT 절에서 그룹함수를 사용한다 해서, 꼭 GROUP BY를 사용해야하는 것은 아니다.
SELECT
MAX(EMPNO)
FROM
EMP;
2. MAX와 AVG와 같은 그룹함수는 무한 중첩이 가능하다.
3. GROUP BY로 묶어진 GROUP 에 대한 조건은 HAVING에 넣어줘야 한다. WHERE에 넣으면 안된다.
4. HAVING은 그 내부에 그룹함수를 포함할 수 있고, 아닐 수도 있다.
5. FROM 절의 SubQuery를 Inline View라 한다.
SELECT 절의 SubQuery는 PIVOT 처럼, 내가 보고자 하는 정보를 한 줄에 모아서 보기 위한 용도로 사용한다.
SELECT
(SELECT COUNT(*) FROM EMP WHERE DEPTNO = 10) as CNT10,
(SELECT COUNT(*) FROM EMP WHERE DEPTNO = 20) as CNT20,
(SELECT COUNT(*) FROM EMP WHERE DEPTNO = 30) as CNT30
FROM
DUAL;
LEFT OUTER JOIN 부분이 약점이었다.
SELECT
d.DEPTNO,
d.DNAME,
CASE COUNT(e.empno)
WHEN 0 THEN '없음'
ELSE TO_CHAR(COUNT(e.empno))
END as 사원수
FROM
DEPT d
LEFT OUTER JOIN
EMP e
ON
d.deptno = e.deptno
GROUP BY
d.DEPTNO, d.DNAME
ORDER BY
d.DEPTNO ASC;
- DEPT와 EMP 를 JOIN 할 때,
DEPT에는 40번 부서가 있지만 EMP에는 40번 부서를 가진 사람이 없는 상황이다.
- 이 때, INNER JOIN ( EQUI JOIN )을 걸게 되면,
40번 DEPT 번호가 누락된다.
- 만약, 40번 DEPT번호를 보여주려면 DEPT를 기준으로 LEFT OUTER JOIN을 걸어줘야 한다.
- 또한, COUNT는 그룹함수이고 이에 GROUP BY를 사용했으니,
해당하는 일반 컬럼들은 GROUP BY 절에 명시해주어야 한다.
컬럼에 대한 '조건'을 걸어 출력할 수 있다.
DECODE
( 프로그래밍 언어에서의 SWITCH문 처럼 사용할 수 있다 )
- 단일 값에 대한 동등 조건만 조건으로 사용할 수 있다.
CASE - WHEN - THEN - END
( 프로그래밍 언어에서의 IF 문처럼 사용할 수 있다. )
- 특정 값의 범위를 조건으로 줄 수도 있다.
SELECT
d.DEPTNO,
d.DNAME,
CASE COUNT(e.empno)
WHEN 0 THEN '없음'
ELSE TO_CHAR(COUNT(e.empno))
END as 사원수
FROM
DEPT d
LEFT OUTER JOIN
EMP e
ON
d.deptno = e.deptno
GROUP BY
d.DEPTNO, d.DNAME
ORDER BY
d.DEPTNO ASC;
SELECT
d.DEPTNO,
d.DNAME,
DECODE(COUNT(e.empno),
0, '없음',
TO_CHAR(COUNT(e.empno))) as 사원수
특정값이 어떤 값 1일 때, 어떤 값A 출력
...
아무것도 포함되지않을때의 값) 과 같이 사용한다.
FROM
DEPT d
LEFT OUTER JOIN
EMP e
ON
d.deptno = e.deptno
GROUP BY
d.DEPTNO, d.DNAME
ORDER BY
d.DEPTNO ASC;
SELECT
(CASE
WHEN DEPTNO = 10 THEN '십'
WHEN DEPTNO = 20 THEN '이십'
WHEN DEPTNO = 30 THEN '삼십'
ELSE '아니다'
END) AS nn
FROM
DEPT;
SELECT
(CASE
WHEN SAL BETWEEN 1000 AND 2000 THEN '1'
WHEN SAL BETWEEN 2000 AND 3000 THEN '2'
ELSE '99'
END) AS nn,
SAL
FROM
EMP;
SELECT
d.DEPTNO,
(CASE
WHEN AVG(e.SAL) >= 2000 THEN '초과'
WHEN AVG(e.SAL) < 2000 THEN '미만'
END) as 평균급여
FROM
DEPT d
JOIN
EMP e
ON
d.deptno = e.deptno
GROUP BY
d.DEPTNO;
SELF JOIN 문법 복습
SELECT
e.empno,
e.ename,
m.mgr as mgrno,
m.ename as mgrname
FROM
EMP e
JOIN
EMP m
ON
e.mgr = m.empno
WHERE
e.sal >= m.sal;
올림, 내림, 반올림
- CEIL(value) : 소수점 첫째자리에서 올림한다.
- FLOOR(value) : 소수점 첫째자리에서 내림한다.
- ROUND(value, n) : 반올림하여 소수점 n째자리까지 표현한다.
- TRUNC(value, n) : 내림하여 소수점 n째자리까지 표현한다.
SELECT
EMPNO,
ENAME,
HIREDATE,
CEIL((SYSDATE - HIREDATE)/365) as 근무년수
FROM
EMP
WHERE
(CEIL((SYSDATE - HIREDATE)/365)) >= 30;
SELECT
EMPNO,
ENAME,
HIREDATE,
ROUND((SYSDATE - HIREDATE)/365) as 근무년수
FROM
EMP
WHERE
(ROUND((SYSDATE - HIREDATE)/365)) >= 30;
SELECT
EMPNO,
ENAME,
HIREDATE,
FLOOR((SYSDATE - HIREDATE)/365) as 근무년수
FROM
EMP
WHERE
(FLOOR((SYSDATE - HIREDATE)/365)) >= 30;
SELECT
EMPNO,
ENAME,
HIREDATE,
TRUNC((SYSDATE - HIREDATE)/365, 1) as 근무년수
FROM
EMP
WHERE
(TRUNC((SYSDATE - HIREDATE)/365)) >= 30;
! 사실 위의 쿼리에는 큰 문제가 있다. 왜냐하면, WHERE절에서 원본 컬럼을 변경했기 때문이다. 해당 쿼리는 HIREDATE에 대해 생성된 INDEX를 타지 못하고, 원본 테이블을 Full-Scan 한다.
- 이 문제를 어떻게 해결할 수 있는가? ( ★★★★★★ )
: 'Inline View'를 활용하자.
즉, 테이블을 잘라낸 이후에 '그 테이블'에 생성된 컬럼에 대해 WHERE절에 조건을 넣어주자.
: 테이블을 한번 잘라서 포장해서 새로운 테이블로 만들어, 거기에만 쿼리를 넣어주는 형식이다.
SELECT
*
FROM
(
SELECT
EMPNO,
ENAME,
HIREDATE,
FLOOR((SYSDATE - HIREDATE)/365) as 근무년수
FROM
EMP
) a
WHERE
a.근무년수 > 30;
GROUP BY ( Column1, Column2, … ColumnN ) 의 의미
**Column1 ~ ColumnN**
까지의 튜플의 조합을 기준으로
Unique한 값을 뽑는다.
- 아래 문제의 예시와 같은 경우,’부서 별’ 유니크한 값을 뽑아야하는데
GROUP BY를 이렇게 사용하면 안된다.
- GROUP BY에 특히, EMPNO ( PK ) 가 들어가면, 그건 GROUP BY를 한 의미가 없다.
- 마치, 서울 시민들을 모아놓아서, 주민번호 별로 그룹화 한것과 같다.
SELECT
EMPNO,
ENAME,
DEPTNO,
MIN(HIREDATE) as HIREDATE
FROM
EMP e
GROUP BY
EMPNO, ENAME, DEPTNO;
SQL 문제 풀이 나머지
SELECT
EMPNO,
ENAME,
DEPTNO,
HIREDATE
FROM
EMP e
WHERE
(e.deptno, e.hiredate)
IN
(
SELECT
DEPTNO,
MIN(HIREDATE) as hiredate
FROM
EMP
GROUP BY
DEPTNO
);
GROUP BY + SubQuery 조합의 문제들
EMP 테이블에서 부서 인원이 4명보다 많은 부서의 부서번호, 인원수, 급여의 합을 출력하시오
SELECT
DEPTNO as 부서번호,
COUNT(EMPNO) as 인원수,
SUM(SAL) as 급여의_합
FROM
EMP
GROUP BY
DEPTNO
HAVING
COUNT(EMPNO) > 4;
- 그룹에 대한 조건을 줄 때는 HAVING절을 사용해야함을 복습했다.
EMP 테이블에서 가장 많은 사원이 속해있는 부서번호와 사원수 출력
SELECT
DEPTNO,
사원수
FROM (
SELECT
DEPTNO,
COUNT(EMPNO) as 사원수
FROM
EMP
GROUP BY
DEPTNO
ORDER BY
사원수 DESC )
WHERE
ROWNUM <= 1;
SELECT
DEPTNO,
COUNT(EMPNO) as 사원수
FROM
EMP
GROUP BY
DEPTNO
HAVING
COUNT(EMPNO) = (
SELECT
MAX(COUNT(EMPNO)) as 사원수max
FROM
EMP
GROUP BY
DEPTNO
);
EMP 테이블에서 가장 많은 사원을 가진 MGR(★)
- 오늘 푼, GROUP BY와 SubQuery를 응용하는 예제 중 가장 어려웠다.
SELECT
EMPNO
FROM
EMP
WHERE
EMPNO = (
SELECT
MGR
FROM
EMP
GROUP BY
MGR
HAVING
COUNT(MGR) = (
SELECT
MAX(COUNT(MGR))
FROM
EMP
GROUP BY
MGR
)
);
**MAX(COUNT(MGR))**
을 통해 계산할 때, **GROUP BY**
를 써주는 것을 하지 못했다.
- 부분 부분 잡아서 끼워넣고,
확인하는게 낫다.
아무리 실력이 뛰어나도
한 블럭씩 해보면서 진행하는게 실수를 안한다.
- 1-Depth의 서브쿼리까지는 간단히 되더라도,
여러 Depth의 서브쿼리를 하려 하니 문제가 어려웠다.
- 앞으로도 서브쿼리는
**Bottom-Up**
방식으로 조금씩 완성시켜가며 진행하자.
SELECT
MGR
FROM
EMP
GROUP BY
MGR
HAVING
COUNT(EMPNO) = (
SELECT
MAX(COUNT(MGR))
FROM
EMP
GROUP BY
MGR
);