[내배캠] 데이터분석 6기/사전캠프 기록

[사전캠프 16일차] 아티클 스터디⑪, SQL 과제, SQLD 공부

물맨두 2025. 2. 14. 19:22
오늘은 사전캠프 마지막 날

오늘 한 일은,

  • 마지막 아티클 스터디 진행하기
  • SQL 공부
    • 개인 퀘스트(달리기반) - [SQL 실전!] Lv7. 예산이 가장 큰 프로젝트는?
    • [SQLD 자격증 챌린지] 모의고사 풀기
    • [SQLD 자격증 챌린지] 14주차 수강하기

 

아티클 스터디 ⑪: 데이터를 쉽게 찾고 잘 활용할 수 있는 기반을 만드는, 토스 Data Analytics Engineer

오늘 읽은 아티클 : 

 

토스의 데이터를 쉽게 찾고 잘 활용할 수 있는 기반을 만드는, Data Analytics Engineer

데이터 활용을 깊게 고민하고 더 나은 데이터 환경을 만들기 위한 노력을 하는 토스 Data Analytics Engineer 분들의 이야기를 들어 보았어요.

toss.im

 

250214 아티클 스터디를 하며 개인 요약 한 일부

오늘은 토스의 DAE분들의 인터뷰를 읽으며 현업에서 어떤 식으로 업무를 바라보고 업무를 수행하는지를 알 수 있었다. 읽으면서 내일배움캠프를 통해 나는 어떤 경험을 쌓기 위해 노력하고 실행해야 할까에 대해 고민하게 됐다. 본캠프에선 팀 프로젝트를 진행하게 될 텐데 그 과정 속에서 나는 무엇을 염두에 둬야 할 때 마케터로서 고민해야 할 지점들은 무엇일까 생각했다. 아무래도 그를 알기 위해선 ①직접 마케터로 일하면서 체득하거나 ②현업 마케터들이 한 고민들을 보거나 하는 2가지 방법이 있다. 그래서 내배캠 기간 동안 ②번의 방법을 실천하기 위해서 어떤 창구가 있을지 찾아봐야겠다. 매주 관심 산업에 있는 마케터가 쓴 책이나 데이터 분석을 마케터 관점에서 담은 책을 일주일에 한 권씩 읽는 것으로 해야 할까?

 


 

SQL 과제 : 개인 퀘스트(달리기반) - [SQL 실전!]

Lv7. 예산이 가장 큰 프로젝트는?

Employees 테이블 :

EmployeeID Name Department Salary
1 Alice HR 5000
2 Bob IT 7000
3 Charlie IT 6000
4 David HR 4500
5 Eve Sales 5500
6 Frank IT 7200

 

Projects 테이블 :

ProjectID ProjectName Budget
101 Alpha 10000
102 Beta 15000
103 Gamma 12000
104 Delta 8000

 

EmployeeProjects 테이블 :

EmployeeID ProjectID
1 101
2 101
3 102
4 103
5 104
6 102
6 103

250214 오늘도 SQL 과제를 위해 테이블을 일일이 만들었다네~

 

Q1. 각 직원이 속한 부서에서 가장 높은 월급을 받는 직원들만 포함한 결과를 조회하기
      (결과 테이블에 직원의 이름, 부서, 월급이 포함되어야 함)

SELECT Name,
       Department,
       MAX(Salary)
FROM employees
GROUP BY Department; ---이렇게 적었는데 GROUP BY 표현이 아니라고 에러 뜸

처음엔 이렇게 적었는데 에러가 떴다. 사실 아직도 이게 왜 안 된다는 건지 이해하지 못함.. 

 

SELECT Name,
       Department,
       MAX(Salary) OVER (PARTITION BY Department) MaxSalary
FROM employees
WHERE Salary = MaxSalary; ---이번엔 MaxSalary가 유효하지 않은 식별자라고 에러 뜸

두 번째 시도는 이것이다. 식별자의 문제래서 다음과 같이 고쳤다.

SELECT a.Name,
       a.Department,
       a.Salary
FROM
(
    SELECT Name,
           Department,
    	   Salary,
    	   MAX(Salary) OVER (PARTITION BY Department) MaxSalary
	FROM employees
) a
WHERE a.Salary = a.MaxSalary;

그랬더니 이렇게 원하는 대로 결과테이블이 출력됐다.

 

Q2. 직원이 참여한 프로젝트 중 예산이 10,000 이상인 프로젝트만 조회하기
       (결과 테이블에는 직원 이름, 프로젝트 이름, 프로젝트 예산이 포함되어야 함)

SELECT e.Name,
       a.ProjectName,
       a.Budget
FROM
(
    SELECT *
	FROM Projects
	WHERE Budget >= 10000
) a LEFT JOIN EmployeeProjects ep ON a.ProjectID = ep.ProjectID
    LEFT JOIN Employees e ON ep.EmployeeID = e.EmployeeID
ORDER BY a.ProjectName

이번엔 에러 뜨는 일 없이 작성했다.

 

우선 문제에서 주어진 대로 예산이 10,000 이상인 프로젝트만 Projects 테이블에서 조회했다.

그리고 이 테이블을 서브쿼리 a로 묶어서 EmployeeProjects 테이블과 JOIN 하고, 또 거기에 Employees 테이블까지 JOIN 했다.

이 상태에서 결과 테이블에 요구한 컬럼들인 직원 이름, 프로젝트 이름, 프로젝트 예산을 SELECT 절에 적고서,

마지막으로 ORDER BY 절을 추가해서 프로젝트 순으로 결과 테이블에 정렬되도록 했다.

 

 


 

SQLD 공부: [SQLD 자격증 챌린지] 수강하기

우선 14주차 강의는 모의고사 문제풀이라서 수강하기 전에 모의고사를 풀었다.

 

첫 SQLD 모의고사! 

SQLD는 50문제를 90분 안에 풀면 된다. 근데 풀다보니 시간이 문제가 아니라 내 머리가 문제..

250214 이야~ 틀린 문제가 도대체 몇 개야

찍는 문제가 많을 때부터 푸는 데 의의를 두기로 했다.

그리고 채점하기 전만 해도 '어차피 오답만 볼 거니까~' 생각했는데 그 오답이 점점 늘어나는 걸 보면서 모의고사를 참 많이 풀어야겠다고 되뇌었다.

 

이제 오답풀이 가보자고! 

 


SQLD 제37회 기출 문제 오답노트

Q1. 다음에서 설명하는 것은 ER모델 중 어떤 항목에 대한 설명인가?

  • "모든 릴레이션은 원자값을 가져야 한다"
    • 릴레이션 = 테이블
    • 테이블에 있는 데이터가 더 이상 쪼개질 수 없는 명확한 1개의 값만 가지고 있어야 함
  • "어떤 릴레이션에서 속성 값이 가질 수 있는 값의 범위를 의미한다"
    • 어떤 테이블에서 컬럼에 들어갈 수 있는 값의 범위를 지정해준다
    • = 도메인(Domain)

 

Q3. 다음 보기 중 슈퍼/서브타입 데이터 모델의 변환 타입에 대한 설명으로 옳은 것은?

  • 슈퍼/서브타입 데이터 모델의 변환 타입의 3가지
    • ①One to One : 슈퍼타입, 서브타입 테이블들을 각각 개별 테이블로 구성 JOIN 발생
    • ②Plus : 각 서브타입에 슈퍼타입을 합해 슈퍼+서브 테이블로 구성 JOIN 발생
    • ③Single : 전체를 하나의 테이블로 통합 테이블이 하나
  • ⑴One to One이란 개별로 발생되는 트랜잭션에 대해서는 개별 테이블로 구성하고 테이블 수가 많아진다. → ⭕
  • ⑵❌ (∵Plus Type은 JOIN 발생함)
  • ⑶❌(∵Plus Type은 JOIN이 발생하기에 JOIN 성능이 우수하지 않음, 그리고 관리가 어려워 항상 사용하지 않음)
  • ⑷❌(∵One to One Type은 JOIN 성능 우수하지 않음, 테이블 수가 많으니 관리가 편하지도 않음)

 

Q7. 다음 주어진 ERD 관계에 대한 설명으로 옳지 않은 것은?

  • ⑴⭕: ACCOUNT에서 GROUP을 바라볼 때, GROUP 쪽에 | 표시만 있다는 것은 1개를 가진다는 의미다
  • ⑵❌(∵점선은 비식별관계를 의미함)

 

Q9. 다음은 데이터베이스 모델링 시에 성능을 고려한 모델링 활동이다. 성능을 고려한 데이터베이스 모델링 단계에서 가장 처음으로 수행해야 할 것과 가장 마지막으로 수행해야 할 것은?

  • 데이터베이스 모델링 순서 :
    • ①정규화 수행
    • → ②용량을 산정
    • → ③트랜잭션 유형을 분석
    • → ④트랜잭션 유형에 따른 반정규화 수행
    • → ⑤기본키와 외래키 조정 및 수퍼타입과 서브타입 조정
    • ⑥ 데이터 모델 검증 및 확인

 

Q11. 다음 보기 중 해시조인(Hash Join)에 대한 설명으로 옳지 않은 것은?

  • Hash Join : 작은 테이블과 큰 테이블이 있을 때, 작은 테이블을 먼저 읽어서 똑같은 값을 큰 테이블 내에서 찾아내는 기법
  • ⑴⭕(∵동등조인은 같은 값을 찾아내는 것임)
  • ⑶❌(∵해시조인은 대용량 데이터를 찾는데 유용한 기법, 온라인 트랜잭션 처리는 실시간으로 처리하기 위해 빨라야 하므로 안 됨)

 

Q12. 다음 보기 중 Join기법에 대한 설명으로 가장 적절한 것은?

  • ⑴⭕: "먼저 나오는 테이블의 선택도가 낮은 테이블…" = 선행테이블이 데이터의 레코드 개수가 적은 것을 택하는 것이 유리함
    • Nested Loop Join : 선행 테이블의 처리 범위를 하나씩 접근하면서 추출된 값으로 후행 테이블을 조인하는 방식
    • OLTP : 실시간으로 하는 것
  • ⑵❌(∵Sort Merge Join은 동등뿐만 아니라 비교연산자로도 가능함)
  • ⑶❌(∵Hash Join에서 행의 수가 큰 테이블을 선행 테이블로 사용하면 Hash Area사이즈가 커짐)

 

Q13. 다음 주어진 테이블에 대해서 아래와 같은 결과값이 나오도록 SQL문의 빈칸에 들어갈 수 있는 내용을 고르시오.

[T_TEST]
DEPTNO   JOB    SAL
--------------------
10     CLERK    1300
10     MANAGER  2150
20     CLERK    1900
20     ANALYST  6000
20     MANAGER  2000

[결과]
DEPTNO   JOB    SUM(SAL)
------------------------
10     CLERK    1300
10     MANAGER  2150
10              3450
20     CLERK    1900
20     ANALYST  6000
20     MANAGER  2000
20              9900  
      	        13350
[SQL]
SELECT DEPTNO, JOB, SUM(SAL)
FROM T_TEST
GROUP BY (           );

  • 보기 중 GROUP BY절에 사용해서 총계를 볼 수 있는 함수는 ROLLUPCUBE만.
    • ⑴❌(∵그냥 GROUP BY절만 사용하면 그룹별로 묶어서 값만 보여줄 뿐)
    • ⑵❌(∵GROUPING SETS() 함수는 그룹별 소계만 보여줌. 누적합은 X)
  • ⑶⭕: ROLLUP() 함수는 소계와 총계를 연산함
  • ⑷❌(∵CUBE() 함수는 모든 케이스를 전부 보여줌)

 

Q14. 주어진 두 개의 테이블에서 대해서 아래의 SQL문을 수행한 이후에 TEST1 테이블의 건수는?

[TEST1]
COL1  COL2  COL3
------------------
A      X      1
B      Y      2
C      Z      3

[TEST2]
COL1  COL2  COL3
------------------
A      X      1
B      Y      2
C      Z      3
D      가      4
E      나      5

[SQL]
MERGE INTO TEST1
USING TEST2
 ON (TEST1.COL1 = TEST2.COL1)
WHEN MATCHED THEN 
	UPDATE SET TEST1.COL3 = 4     
		WHERE TEST1.COL3 = 2 
	DELETE WHERE TEST1.COL3 <= 2
WHEN NOT MATCHED THEN 
	INSERT(TEST1.COL1, TEST1.COL2, TEST1.COL3)
	VALUES(TEST2.COL1, TEST2.COL2, TEST2.COL3);

  • SQL문 해석 :
    • TEST1 테이블과 TEST2 테이블을 합쳐라
    • TEST1 테이블의 COL1 컬럼의 값과 TEST2테이블의 COL1 컬럼의 값이,
      • 일치하면
        • TEST1테이블의 COL3의 값이 2일 때, TEST1테이블의 COL3의 값을 4로 바꿔라
        • 그리고서 TEST1테이블의 COL3의 값이 2 이하일 때, 값을 지워라 
      • 일치하지 않으면
        • TEST1의 COL1에 TEST2의 COL1 값을 넣고,
          TEST1의 COL2에 TEST2의 COL2 값을 넣고,
          TEST1의 COL3에 TEST2의 COL3 값을 넣어라
  • SQL문 실행에 따라 변하는 내용 :
    • TEST1 테이블에 COL1=B인 행의 COL3 값이 4가 됨
    • TEST1테이블에 TEST2에서 COL1=D, COL1=E인 행들의 데이터가 그대로 추가됨
  • 그래서 TEST1 테이블의 건수는 5개!!

 

Q15. 다음은 ABC기업에 대한 데이터베이스 모델링이다. 다음의 설명 중에서 올바른 것은?

  • ⑵⭕: 제품마스터와 주문이력은 둘의 기본키도 다르고 연결되어 있지도 않음. 이런 상황에서 둘을 한 번에 조인하려고 하면 카텐시안 곱이 발생함.
  • ⑶❌(∵주어진 ERD에선 점선이 아닌 모두 실선으로 표현돼 있어 식별관계라는 걸 알 수 있음)
  • ⑷❌(∵최소 2개임)

 

Q17. 다음 주어진 SQL문을 수행하였을 때의 결과가 아래와 같을 때 빈칸에 들어갈 것으로 알맞은 것은?

[SQL]
SELECT 10+20 * (( ? )(NULL, 0.1, 0.2))
FROM. DUAL;
[RESULT]
14

  • 보기 ⑴, ⑵는 매개변수를 2개 받음
  • NVL2(조건, 값1, 값2) : 조건을 확인했을 때 NULL값이 아니면 값1을, NULL값이면 값2를 출력
  • COALESCE() : 들어오는 값들 중에 NULL값이 아닌 값 중에 첫 번째 값을 출력

 

Q18. 보기에서 SELECT 결과가 NULL이 아닌 경우는?

  • ⑴⭕(∵COALESCE() 함수는 주어진 값들 중에서 NULL값 아닌 것 중에 첫 번째 값을 출력하니까 1이 나옴)
  • ⑶❌(∵DECODE()함수는 첫 번째 값과 두 번째 값이 같으면 세 번째 값을, 아니면 네 번째 값을 출력하니까 A≠B이므로 NULL값이 나옴)
  • ⑷❌(∵NULLIF() 함수는 첫 번째 값과 두 번째 값이 같으면 NULL값을 출력하니까 A=A이므로 NULL값이 나옴)

 

Q21. 다음 주어진 테이블에서 집계 함수를 수행하였을 때 결과값으로 다른 것을 고르시오.

[TEST21]
USERID    USERCOUNT
-------------------
KIM        10
PARK       20
LIM        NULL
SIN        NULL
1) SELECT COUNT(NVL (USERCOUNT, 0)) FROM TEST21;
2) SELECT SUM(NVL (USERCOUNT, 0)) / 4 FROM TEST21;
3) SELECT AVG(NVL (USERCOUNT, 0)) FROM TEST21;
4) SELECT AVG(NVL (USERCOUNT, 1)) - 0.5 FROM TEST21;
  • ⑴ 4 (∵USERCOUNT가 NULL값이면 0으로 대체하랬으니까 10, 20, 0, 0을 세면 4)
  • ⑵ 7.5 (∵USERCOUNT에서 NULL값을 0으로 대체하면 10, 20, 0, 0이고 이를 모두 더해서 4로 나누면 7.5)
  • ⑶ 7.5 (∵⑵과 같은 연산임)
  • ⑷ 7.5 (∵USERCOUNT에서 NULL값을 1로 대체하면 10, 20, 1, 1이고 평균을 내서 0.5를 빼면 8-0.5는 7.5)

 

Q22. 다음 파티션에 대한 설명으로 틀린 것을 고르시오.

1) RANK() OVER (PARTITION BY JOB ORDER BY 급여 DESC) JOB_RANK
   #직업별 급여가 높은 순서대로 순위가 부여되고 동일한 순위는 동일한 값이 부여 된다.
2) SUM(급여) OVER (PARTITION BY MGR ORDER BY 급여 RANGE UNBOUNDED PRECEDING)
   #RANGE는 논리적 주소에 의한 행 집합을 의미하고 MGR별 현재 행부터 파티션내 첫번째 행까지 급여의 합계를 계산한다.
3) AVG(급여) OVER (PARTITION BY MGR ORDER BY 날짜 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
   #각 MGR 별로 앞의 한건, 현재 행, 뒤의 한건 사이에서 급여의 평균을 계산한다.
4) COUNT(*) OVER (ORDER BY 급여 RANGE BETWEEN 10 PRECEDING AND 300 FOLLOWING)
   #급여를 기준으로 현재 행에서의 급여의 -10 ~ +300사이의 급여를 가지는 행의 수를 COUNT
  • ⑵MGR 기준으로 파티션을 주고서 급여 기준으로 정렬할 때,
    • PRECEDING UNBOUNDED : 현재 행 기준에서 (파티션 내) 첫 번째 행부터 쭈욱~
  • ⑶MGR 기준으로 파티션을 주고서 날짜 기준으로 정렬할 때,
    • BETWEEN 1 PRECEDING AND 1 FOLLOWING : 현재 행 기준에서 하나 앞선 행부터 한 행 뒤에 있는 것까지
    • 근데 ⑶번 선지에서 "ORDER BY 날짜"에 대한 설명은 없어서 ⑶번이 틀린 선지
  • ⑷급여 기준으로 정렬할 때,
    • 10 PRECEDING AND 300 FOLLOWING : 현재 행의 급여에서 -10인 값부터 +300인 값까지

 

Q23. 다음 주어진 테이블에서 아래의 SQL을 수행한 결과로 알맞은 것은?

[TEST23]
COL1   COL2   COL3   COL4
-------------------------
10     10     10     20
20     20     NULL   30
30     NULL   NULL   10
NULL   30     10     40

[SQL]
SELECT SUM(COL1+COL2+COL3+COL4) FROM TEST23;
SELECT SUM(COL1) + SUM(COL2) + SUM(COL3) + SUM(COL4) FROM TEST23;

  • SUM(COL1+COL2+COL3+COL4)을 연산할 때 NULL값이 포함돼 있으면 연산하지 않음.
    • 그래서 첫 번째 행의 10+10+10+20 = 50임

 

Q28. 다음 보기 중 아래의 SQL에 대한 설명으로 가장 올바른 것은?

SELECT 분류코드,
       AVG(상품가격) AS 상품가격,
       COUNT(*) OVER (ORDER BY AVG(상품가격)
                      RANGE BETWEEN 10000 PRECEDING AND 10000 FOLLOWING) AS CNT
FROM 상품
GROUP BY 분류코드;

  • ⑴❌(∵윈도우 함수를 GROUP BY절과 함께 쓸 수 있음)
  • ⑶⭕
  • ⑷❌(∵'상품 전체의' 평균상품가격이 아니라 GROUP BY 분류코드에 의해 "분류코드별"이 되는 것이 맞음)

 

Q29. 아래의 테이블들에 대해서 SQL문을 수행하였을 때의 결과값은?

[TEST29_1]
COL
----
1
2
3
4

[TEST29_2]
COL
----
2
NULL

[SQL]
SELECT COUNT(*)
FROM TEST29_1 A
WHERE A.COL NOT IN (SELECT COL FROM TEST29_2);

  • WHERE절의 NOT IN : 
    • NOT IN (2, NULL) = 2가 들어가도, NULL값이 들어가도 안 됨
      • 1 <> 2 : TRUE
      • 1 <>NULL : FALSE (∵같은지 다른지를 따지는 연산에서 NULL값이 들어가면 무조건 FALSE가 뜸)
  • ⑴⭕(∵WHERE절 조건에서 "NOT IN (2, NULL)"에 의해 모두 FALSE가 뜨면서 조건을 성립하는 데이터가 하나도 없음)

(아직 끝난 게 아니다.. 안타깝게도...)


 

아직 틀린 문제들이 한가득 남아있지만, 시간도 늦었고 어차피 15주차 강의도 들어야 하니까 주말 동안 마저 공부하기로 한다.

확실히 문제를 많이 풀어봐야 더 잘 기억에도 남고 이해도 확실히 된다.