공부 순서
- 먼저 SQLD 기출문제집 (SQL 자격검정 실전문제) 을 준비.
- ‘1과목 데이터 모델링의 이해’ (총 50문제)
1과목부터 먼저 풀었는데 처음보는 개념이 많았다. 대부분 이론문제였는데
문제를 맞추는 것 보다 ‘이런 내용이 있고 이런 문제가 나오네’ 하고 파악하는 것을 목표로
문제와 보기를 쭉 훑어보면서 풀었다.
- ‘2과목 SQL 기본 및 활용’ (총 126문제)
1과목을 다 풀고 곧바로 2과목을 풀기 시작했는데 개념을 모르면 아예 풀 수 없는 문제가 많았다.
(집합연산자, Rank, Rollup, Grouping set 등)
그래서 이때부터 개념공부를 병행했다.
- 아래 2가지 개념서가 가장 유명한 것 같았다.


개념정리가 필요하긴 하지만 책을 보면서 공부하기엔 시간이 없을거 같아
개념 정리된 영상 없나 찾아보다가 홍쌤의 데이터랩 채널을 찾았다.


올해 개정된 부분을 반영한 개념정리 영상이 올라와 있어서
1과목(총 1강), 2과목(총 3강) + 특강(NULL, 서브쿼리 등) + 기출문제 풀이 1회차
이렇게 본 것 같다. 약 10시간 분량의 강의인데 스카 가서도 듣고
이동할 때 지하철에서도 듣고 틈날때마다 들은 것 같다.
무료이지만 정리된 핵심 요약의 퀄리티가 매우 좋고, 선생님 강의 전달력도 좋다.
개념정리는 위 채널의 영상만 봐도 충분하다고 생각한다.
(노랭이 문제집만으로 부족한 사람은 홍쌤 사이트에서 추가 기출문제+풀이 구매가 가능하다.
고득점을 목표로 한다면 구매해서 풀어봐도 좋을 거 같다.)
- 노트 및 노션 정리
→ 강의 보면서 좀 헷갈리는 내용이나 노랭이 문제에서 봤던 개념이 있으면 캡쳐해서 정리했다.
[ 1과목 개념 캡쳐정리 ]
SQLD 1과목 개념정리[ 2과목 ]
2과목 부터는 모르는 내용 투성이라 캡쳐는 따로 안하고 쭉 영상을 집중해서 봤다.

강의 들으면서 공책에 그려보면서 이해하려고 했다.
답안지를 봐도 이해가 안되는 문제들이 있었는데

그럴 땐 유튜브, 구글에 문제풀이를 검색해서 찾아보거나 챗지피티한테 물어봤다.
또 쿼리가 복잡하거나 매우 긴 문제들이 있었는데 그럴땐
- 테이블 과 데이터를 직접 생성해서 연습해보았다.

연습한 쿼리들
select 24*123 from dual;
select deptno, job,
decode(deptno, 10,
decode(job, 'CLERK', 'A', 'B'),
'c') AS RESULT
from emp;
// 오라클은 소문자 대문분
SELECT * FROM EMP WHERE ENAME = 'smith';
select empno, ename, sal from emp where sal >=1500;
select empno, ename, sal, comm from emp where comm is null;
select empno, ename, sal, comm, deptno from emp where deptno = 10 AND sal >= 2000 OR empno = 7782;
select deptno, max(sal), avg(sal)
from emp
group by deptno
having avg(sal) > 1000;
select deptno, sum(sal)
from emp
where sal > 2000
group by deptno;
// 그룹함수를 사용해서 조건을 주고싶으면 having 절에 주어야 한다.
select deptno, sum(sal)
from emp
group by deptno
having sum(sal) > 9000;
// 그룹함수는 허가되지 않습니다. where 절에는 그룹함수를 사용할 수 없다.
Select Deptno, Sum(Sal)
From Emp
Where Sum(Sal) > 9000
Group By Deptno;
// 테이블 추가
create table t1
(
no number(1) not null,
name varchar2(1),
constraint t1_pk primary key (no)
);
create table t2
(
no number(1) not null,
name varchar2(1),
constraint t2_pk primary key (no)
);
drop table t1;
drop table t2;
create table t1
(
no number(1),
name varchar2(1)
);
create table t2
(
no number(1),
name varchar2(1)
);
insert into t1 values (1, 'A');
insert into t1 values (2, 'B');
insert into t1 values (3, 'C');
insert into t1 values (NULL, 'D');
select * from t1;
insert into t2 values (1, 'A');
insert into t2 values (1, 'B');
insert into t2 values (2, 'C');
insert into t2 values (NULL, 'D');
select * from t2;
// equi 조인 (등가 조인)
select t1.no, t2.no, t1.name, t2.name
from t1, t2
where t1.no = t2.no;
// 별칭 버전
// 일반 조건은 조인 조건 뒤에 AND로 붙여서 줄 수 있다.
// SQL SERVER와 다르게 ORACLE은 조인 조건을 ON절이 아닌 WHERE절에 준다.
select 티원.no, 티투.no, 티원.name, 티투.name
from t1 티원, t2 티투
where 티원.no = 티투.no
AND 티원.no >=2;
// 부서 테이블 추가
CREATE TABLE DEPT
(
DEPTNO NUMBER(2) NOT NULL,
DNAME VARCHAR2(10),
LOC VARCHAR2(20)
);
INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON');
// EMP 테이블과 DEPT 테이블을 사용하여 각 직원의 이름과 부서명을 함께 출력
SELECT EMP.ENAME, EMP.DEPTNO, DEPT.DNAME
FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;
// 연결 조건을 쓰지 않으면 크로스 조인이 발생
SELECT EMP.ENAME, EMP.DEPTNO, DEPT.DNAME
FROM EMP, DEPT;
// SALGRADE TABLE 추가
CREATE TABLE SALGRADE
(
GRADE NUMBER(1) NOT NULL,
LOSAL NUMBER(4),
HISAL NUMBER(4)
);
INSERT INTO SALGRADE VALUES (1, 700, 1200);
INSERT INTO SALGRADE VALUES (2, 1201, 1400);
INSERT INTO SALGRADE VALUES (3, 1401, 2000);
INSERT INTO SALGRADE VALUES (4, 2001, 3000);
INSERT INTO SALGRADE VALUES (5, 3001, 9999);
// NON EQUI JOIN 연습 - BETWEEN AND
SELECT E.ENAME, E.SAL, S.GRADE
FROM EMP E, SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL;
// SELF JOIN 연습 - 매니저보다 급여가 많은 직원 출력
SELECT E1.EMPNO, E1.ENAME, E1.SAL,
E2.EMPNO, E2.ENAME, E2.SAL
FROM EMP E1, EMP E2
WHERE E1.MGR = E2.EMPNO // 셀프 조인 조건
AND E1.SAL > E2.SAL; // 조인 후에 필터링 하는 조건
// 표준 조인
// 표준 조인 중에서 ANSI 표준으로 작성한 INNER JOIN
SELECT EMP.ENAME, DEPT.DNAME
FROM EMP INNER JOIN DEPT
ON (EMP.DEPTNO = DEPT.DEPTNO)
ORDER BY DEPT.DNAME ;
// 조인할 컬럼명이 같을 경우 ON절 대신 USING을 사용할 수도 있다.
SELECT EMP.ENAME, DEPT.DNAME
FROM EMP JOIN DEPT
USING (DEPTNO);
// NATURAL JOIN
SELECT *
FROM T1 NATURAL JOIN T2; // NO 와 NAME이 둘다 동일한 경우만 출력됨
SELECT EMP.ENAME, DEPT.DNAME
FROM EMP NATURAL JOIN DEPT;
// OUTER JOIN 연습
CREATE TABLE PROFESSOR
(
PROFNO NUMBER(4),
NAME VARCHAR2(20),
POSITION VARCHAR2(20),
DEPTNO NUMBER(3)
);
CREATE TABLE STUDENT2
(
STUDNO NUMBER(4),
NAME VARCHAR2(20),
GRADE NUMBER(1),
TEL VARCHAR2(20),
PROFNO NUMBER(4)
);
DROP TABLE STUDENT2;
DROP TABLE PROFESSOR;
INSERT INTO STUDENT2 VALUES (5001, '강성근', 4, '010-1234-1234', 1001);
INSERT INTO STUDENT2 VALUES (5002, '고대영', 4, '010-1234-1234', 2001);
INSERT INTO STUDENT2 VALUES (5003, '권정현', 4, '010-1234-1234', 3002);
INSERT INTO STUDENT2 VALUES (5004, '김정일', 4, '010-1234-1234', 4001);
INSERT INTO STUDENT2 VALUES (5005, '김재현', 4, '010-1234-1234', 4003);
INSERT INTO STUDENT2 VALUES (8001, '이유나', 1, '010-1234-1234', NULL);
INSERT INTO STUDENT2 VALUES (8002, '이민정', 1, '010-1234-1234', NULL);
INSERT INTO STUDENT2 VALUES (8003, '전영훈', 1, '010-1234-1234', NULL);
INSERT INTO STUDENT2 VALUES (8004, '허민기', 1, '010-1234-1234', NULL);
INSERT INTO PROFESSOR VALUES (1001, '강경연', '정교수', 101);
INSERT INTO PROFESSOR VALUES (1002, '경윤영', '조교수', 101);
INSERT INTO PROFESSOR VALUES (1003, '김다훈', '전임강사', 101);
INSERT INTO PROFESSOR VALUES (2001, '노경우', '전임강사', 102);
INSERT INTO PROFESSOR VALUES (2002, '이용준', '조교수', 102);
INSERT INTO PROFESSOR VALUES (2003, '장요한', '정교수', 102);
INSERT INTO PROFESSOR VALUES (3001, '전홍범', '정교수', 103);
INSERT INTO PROFESSOR VALUES (3002, '정성용', '조교수', 103);
INSERT INTO PROFESSOR VALUES (3003, '백승윤', '전임강사', 103);
INSERT INTO PROFESSOR VALUES (4001, '한병두', '정교수', 201);
INSERT INTO PROFESSOR VALUES (4002, '현기숙', '조교수', 201);
INSERT INTO PROFESSOR VALUES (4003, '오하영', '조교수', 202);
INSERT INTO PROFESSOR VALUES (4004, '김만중', '전임강사', 202);
// LEFT OUTER JOIN 예제
// STUDENT2 테이블과 PROFESSOR 테이블을 조인하여
// 1, 4학년 학생들의 이름, 학년, 지도교수이름 출력
SELECT S.NAME, S.GRADE, P.NAME
FROM STUDENT2 S, PROFESSOR P
WHERE S.PROFNO = p.PROFNO
AND (S.GRADE = 1 OR S.GRADE = 4); // 괄호 안쓰면 카타시안 곱 발생
// 위처럼 하면 오라클 기본 조인인 INNER JOIN으로 출력됨
// 오라클에서 LEFT OUTER JOIN 사용시
SELECT S.NAME, S.GRADE, P.NAME
FROM STUDENT2 S, PROFESSOR P
WHERE S.PROFNO = P.PROFNO(+)
AND (S.GRADE = 1 OR S.GRADE = 4);
// OR 조건 대신 IN 을 사용할 수도 있다.
SELECT *
FROM STUDENT2 S, PROFESSOR P
WHERE S.PROFNO = P.PROFNO(+)
AND S.GRADE IN (1,4);
// SQL SERVER에서 LEFT OUTER JOIN 사용시
SELECT S.NAME, S.GRADE, P.NAME
FROM STUDENT2 S
LEFT OUTER JOIN PROFESSOR P ON S.PROFNO = P.PROFNO
WHERE S.GRADE = 1 OR S.GRADE = 4;
// ANSI 표준 (IN 사용 버전)
SELECT S.NAME AS 학생명, S.GRADE, P.NAME AS 교수명
FROM STUDENT2 S
LEFT OUTER JOIN PROFESSOR P
ON S.PROFNO = P.PROFNO
WHERE S.GRADE IN (1,4);
// PIVOT 연습
CREATE TABLE UNSTACK_TEST
(
년도 NUMBER(4),
성별 VARCHAR2(10),
지역 VARCHAR2(10),
구매량 NUMBER(1)
);
INSERT INTO UNSTACK_TEST VALUES (2008, '남자', '서울', 1);
INSERT INTO UNSTACK_TEST VALUES (2008, '남자', '경기', 2);
INSERT INTO UNSTACK_TEST VALUES (2008, '여자', '서울', 3);
INSERT INTO UNSTACK_TEST VALUES (2008, '여자', '경기', 4);
INSERT INTO UNSTACK_TEST VALUES (2009, '남자', '서울', 5);
INSERT INTO UNSTACK_TEST VALUES (2009, '남자', '경기', 6);
INSERT INTO UNSTACK_TEST VALUES (2009, '여자', '서울', 7);
INSERT INTO UNSTACK_TEST VALUES (2009, '여자', '경기', 8);
// 성별, 연도별 구매량 총 합을 표한하는 교차표 작성
SELECT *
FROM (SELECT 성별,년도,구매량
FROM UNSTACK_TEST)
PIVOT(SUM(구매량) FOR 년도 IN(2008,2009));
// WIDE 데이터 형식으로 테이블 생성
CREATE TABLE TT5
(
MONTH NUMBER(2),
월 NUMBER(2),
화 NUMBER(2),
수 NUMBER(2),
목 NUMBER(2),
금 NUMBER(2),
토 NUMBER(2),
일 NUMBER(2)
);
INSERT INTO TT5 VALUES (01, 10, 20, 30, 40, 45, 35, 60 );
INSERT INTO TT5 VALUES (02, 39, 45, 75, 34, 65, 45, 33 );
INSERT INTO TT5 VALUES (03, 13, 24, 67, 43, 45, 34, 56 );
INSERT INTO TT5 VALUES (04, 34, 50, 34, 22, 24, 43, 44 );
SELECT * FROM TT5;
// UNPIVOT 연습
// 위의 테이블을 STACK 처리하기
SELECT *
FROM TT5
UNPIVOT (구매건수 FOR 요일 IN(월,화,수,목,금,토,일));
// 구매건수는 VALUE인 데이터의 컬럼명 자리이고 요일은 STACK 만들 컬럼명 자리임
// DML, DDL, TDL, DCL
// 서브쿼리를 사용해서 여러건 insert 하기
CREATE TABLE EMP3
(
EMPNO NUMBER(4) NOT NULL,
ENAME VARCHAR2(10),
DEPTNO NUMBER(2)
);
Insert into EMP3 (EMPNO, ENAME, DEPTNO)
SELECT EMPNO, ENAME, DEPTNO
FROM EMP
WHERE DEPTNO = 20;
SELECT * FROM EMP3;
// UPDATE
UPDATE EMP3
SET DEPTNO = 10
WHERE ENAME = 'smith';
// DDL - CTAS (테이블 복제)
CREATE TABLE EMP2
AS
SELECT * FROM EMP;
SELECT * FROM EMP2;
DROP TABLE EMP2;
// WHERE 조건 줘서 일부 데이터만 복제하기
CREATE TABLE EMP2
AS
SELECT * FROM EMP
WHERE SAL <= 3000;
SELECT * FROM EMP2;
// ALTER
ALTER TABLE EMP2
ADD (BIRTHDAY DATE);
ALTER TABLE EMP2
ADD (BIRTHDAY2 DATE, BIRTHDAY3 DATE);
// 컬럼 생성 시 NOT NULL 속성 전달 불가
ALTER TABLE EMP2
ADD (BIRTHDAY4 DATE NOT NULL);
// 데이터 없이 EMP2 테이블 구조만 복제
CREATE TABLE EMPEX
AS
SELECT * FROM EMP2 WHERE 1=2;
SELECT * FROM EMPEX;
ALTER TABLE EMPEX
ADD (BIRTHDAY4 DATE NOT NULL); // 테이블에 데이터가 없을때는 컬럼 추가시 NOT NULL 설정 가능`
// 컬럼 추가시 default를 추가하면 not null 속성을 갖는 컬럼 추가 가능
ALTER TABLE EMPEX
ADD (FEELING VARCHAR2(30) DEFAULT 'GOOD' NOT NULL);
INSERT INTO EMPEX VALUES (1, 'F', 'F', 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), 1234, 0, 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), DEFAULT);
INSERT INTO EMPEX(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, BIRTHDAY, BIRTHDAY2, BIRTHDAY3, BIRTHDAY4) VALUES (1, 'F', 'F', 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), 1234, 0, 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'));
// FEELING 컬럼을 NULLABLE로 바꾸기
ALTER TABLE EMPEX
MODIFY (FEELING NULL);
INSERT INTO EMPEX(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, BIRTHDAY, BIRTHDAY2, BIRTHDAY3, BIRTHDAY4, FEELING) VALUES (1, 'F', 'F', 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), 1234, 0, 1, TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), TO_DATE('1981-05-01', 'yyyy-mm-dd'), NULL);
create table emploeyee
(
id number(4),
name varchar2(20),
sal number(4),
dname varchar2(100)
);
insert into emploeyee values(0001, '홍길동', 1000, '아시아지부');
insert into emploeyee values(0002, '박길동', 2000, '아시아지부');
insert into emploeyee values(0003, '최길동', 3000, '아시아지부');
insert into emploeyee values(0004, '이길동', 4000, '남유럽지부');
insert into emploeyee values(0005, '김길동', 6000, '남유럽지부');
insert into emploeyee values(0006, '김길동', 8000, '남유럽지부');
select * from emploeyee;
update emploeyee e1
set sal = (select max(sal)
from emploeyee e2
where e1.dname = e2.dname)
where sal <= (select avg(sal) from emploeyee);


시간 부족으로 몇 문제 밖에 못해봤지만 직접 데이터를 넣고
쿼리를 작은 단위로 쪼개서 실행시키면서 왜 이런 결과가 나오는지 이해하려고 노력했다.
공부 시간 & 공부량
일요일 시험이었는데 그 전주 수요일부터 시작해서 약 10일간 하루 3시간 정도 공부했고,
이동시간이나 잠자기전에 틈틈이 강의를 들었다.
노랭이는 백지상태로 1번(풀기), 강의 듣고 1번(정답 확인 및 오답),
시험전날 빠르게 훑으면서 1번(눈으로 복습) 이렇게 총 3번 봤다.
생각보다 생소한 개념이 많이 나온다. 이해하는데 시간이 필요하기 때문에 나처럼 비전공자이면
벼락치기를 하기 보다는 시간을 길게 잡아 조금씩 공부하는게 나은 거 같다.
시험 후기
1과목은 괜찮았고, 2과목은 생각보다 어렵게 느껴졌다.
노랭이 기출에서 본 문제 중에 3개?정도 그대로 나왔던거 같다.
- 정규식 REGEXP_INSTR 함수 문제
SELECT REGEXP_INSTR('12345678','(123)(4(56)(78))', 1, 1, 0, 'i', 2) FROM DUAL;
- UNION ALL 했을 때 첫번째 SELECT문의 컬럼 별칭이 컬럼명으로 출력되는지,
두번째 SELECT문의 컬럼 별칭이 컬럼명으로 출력되는지 선택하는 문제

- 0 으로 나누는 연산에서 300/0 하면 0이 나오는지 error가 나오는지 하는 문제
(0/300, 300/0, 숫자/null > 0, error, null)

- RollUp, GroupingSets 관련 문제
다 기억나지는 않지만 기억나는 것 중에 위 4개가 헷갈리고 어려웠던 거 같다.
총 50문제 중에 2과목에서 10문제 정도 생소하거나 좀 어렵게 느껴졌다.

6일에 사전점수가 나왔는데 합격이다~!
실무하다가 SQLP도 도전해보고 싶다.
SQLD는 기본 개념을 습득하는데 좋은 시험인 거 같다.
다만, 쿼리짜는 실력은 리트코드 같은데서 문제 풀면서 연습하거나
실무를 해야 늘 것 같다.
Share article