본문으로 건너뛰기

단원 모의 문제

SQLD 목차데이터 모델과 SQL문제 10개

2단원 '데이터 모델과 SQL'에서 배운 레슨 다섯 개를 시험 형식의 10문제로 확인합니다. 보기 넷 가운데 하나를 고른 뒤 정답을 펼쳐 맞춰 보고, 틀린 문제는 해설 끝에 적힌 레슨으로 돌아가 다시 봅니다.

해설은 정답의 까닭을 먼저 적고, 나머지 보기를 한 줄씩 짚습니다. 고른 보기의 줄부터 읽으면 어디서 생각이 갈렸는지 알 수 있습니다.

SQL 문제는 레슨과 같은 예제 데이터로 풉니다. EMP의 MGR은 관리자의 사번입니다.

EMP

EMPNOENAMESALDEPTNOMGR
1001김민준52010NULL
1002이서연380201006
1003박지호410201006
1004최유나450301001
1005정하늘300NULL1004
1006강도윤470201001

DEPT

DEPTNODNAMELOC
10기획서울
20개발판교
30영업부산
40인사서울

문제 1

연수 신청 표(사번, 과정코드, 사원이름, 과정명)의 주식별자는 '사번 + 과정코드'입니다. 이 표에서 일어난 일 (가)(나)(다)를 이상 현상과 차례로 짝지은 것으로 알맞은 것을 고르세요.

(가) 과정명을 '엑셀 기초'에서 '엑셀 입문'으로 바꾸다가 그 과정의 줄 가운데 몇 줄을 빠뜨려, 같은 과정이 줄마다 다른 이름으로 남았습니다.

(나) 다음 달에 처음 여는 과정은 아직 신청한 사원이 없어서, 과정코드와 과정명을 이 표에 적어 둘 수 없습니다.

(다) 어떤 과정을 신청한 사원이 한 명뿐이었는데, 그 신청을 취소하며 줄을 지우자 그 과정의 과정명까지 표에서 없어졌습니다.

  • ① 삭제 이상 · 삽입 이상 · 갱신 이상
  • ② 갱신 이상 · 삽입 이상 · 삭제 이상
  • ③ 갱신 이상 · 삭제 이상 · 삽입 이상
  • ④ 삽입 이상 · 삭제 이상 · 갱신 이상
정답

② — (가)는 되풀이해 적힌 과정명을 일부 줄만 고쳐 값이 어긋난 갱신 이상입니다. (나)는 신청이 없는 과정을 따로 넣지 못하는 삽입 이상, (다)는 줄을 지우다 과정명까지 잃은 삭제 이상입니다. 과정명이 주식별자의 일부인 과정코드에만 종속되어(부분 종속) 생기는 일이라, 과정(과정코드, 과정명)을 떼어 내면 세 일이 모두 없어집니다.

  • ① (가)와 (다)를 바꿔 봤습니다. (가)에서 옛 과정명이 남은 것은 지워진 것이 아니라 일부 줄만 고쳐 값이 어긋난 갱신 이상이고, 줄을 지우다 과정명까지 잃은 (다)가 삭제 이상입니다.
  • ③ (나)와 (다)를 바꿔 봤습니다. 둘 다 신청자가 없는 과정 이야기라 헷갈리지만, 넣지 못하는 (나)가 삽입 이상이고 지우다 정보를 잃는 (다)가 삭제 이상입니다.
  • ④ 장면마다 하던 일과 다른 이상을 짝지었습니다. 이상의 이름은 그때 하던 일을 따릅니다. 고치다 어긋나면 갱신, 넣지 못하면 삽입, 지우다 다른 정보까지 잃으면 삭제 이상입니다.

다시 볼 레슨: 정규화

문제 2

납품 표(공급사번호, 부품번호, 공급사명, 지역코드, 지역명, 단가)의 주식별자는 '공급사번호 + 부품번호'입니다. 공급사번호로 공급사명과 지역코드가, 지역코드로 지역명이 정해지고, 단가는 공급사와 부품을 다 알아야 정해집니다. 이 표를 정규화한 설명으로 옳지 않은 것을 고르세요.

  • ① 공급사명은 주식별자의 일부인 공급사번호만 알아도 정해지므로 부분 종속입니다.
  • ② 2정규형으로 나누면 공급사번호로 정해지는 공급사명·지역코드·지역명이 공급사 표로 옮겨 가고, 납품 표에는 공급사번호·부품번호·단가가 남습니다.
  • ③ 3정규형까지 나눈 뒤 공급사 목록을 볼 때마다 지역명을 함께 본다면, 성능 문제가 확인된 곳에서 지역명을 공급사 표에 다시 넣는 것은 반정규화입니다.
  • ④ 단가는 공급사번호와 부품번호를 함께 알아야 정해지므로 이행 종속이고, 3정규형에서 떼어 냅니다.
정답

④ — 단가는 주식별자 '공급사번호 + 부품번호' 전체에 종속됩니다. 주식별자가 아닌 속성을 거쳐 정해지지 않으니 이행 종속이 아니고, 일부에만 기대지도 않아 납품 표에 그대로 남습니다.

  • ① 맞습니다. 공급사명을 정하는 데 부품번호는 쓰이지 않으니, 주식별자의 일부에만 기대는 부분 종속입니다.
  • ② 맞습니다. 2정규형은 부분 종속을 떼어 냅니다. 공급사번호를 따라 정해지는 속성은 지역코드를 거치는 지역명까지 공급사 표로 옮겨 가고, 두 열이 다 있어야 정해지는 단가만 남습니다.
  • ③ 맞습니다. 3정규형에서 지역명은 지역 표로 떨어져 나갑니다. 조회 성능을 위해 일부러 중복을 다시 두는 일이 반정규화이고, 정규화를 먼저 한 뒤 성능 문제가 확인된 곳에만 합니다.

다시 볼 레슨: 정규화

문제 3

EMP를 관리자 쪽 M과 부하 쪽 E로 두고, M을 기준으로 외부 조인합니다. 결과의 행 수로 알맞은 것을 고르세요.

SELECT M.ENAME AS MGR_NAME,
  E.ENAME
FROM EMP M
  LEFT OUTER JOIN EMP E
  ON E.MGR = M.EMPNO
ORDER BY M.EMPNO, E.EMPNO;
  • ① 5행
  • ② 6행
  • ③ 8행
  • ④ 9행
정답

③ — 관리 관계 짝 다섯이 5행이 됩니다(부하가 둘인 김민준·강도윤은 두 줄씩). 여기에 부하가 없는 이서연·박지호·정하늘이 E 쪽 NULL로 한 줄씩 더해져 8행입니다.

  • ① 짝이 있는 줄만 셌습니다. 내부 조인이면 5행이지만, M 기준 외부 조인은 부하가 없는 관리자 쪽 행도 E 칸을 NULL로 채워 남깁니다.
  • ② 외부 조인 결과가 기준 테이블 M의 행 수와 같다고 봤습니다. 부하가 둘인 김민준·강도윤이 두 줄씩 나와, 기준 행 6개보다 많아집니다.
  • ④ 관리자가 없는 김민준도 NULL 줄로 더했습니다. 김민준은 M 쪽에서 보면 부하가 둘인 관리자라 짝 두 줄로만 나오고, NULL 줄은 부하가 없는 세 사람 몫입니다.
MGR_NAMEENAME
김민준최유나
김민준강도윤
이서연NULL
박지호NULL
최유나정하늘
정하늘NULL
강도윤이서연
강도윤박지호

다시 볼 레슨: 관계와 조인

문제 4

모델의 관계를 SQL로 옮기며 판단한 것입니다. 옳은 것을 고르세요.

  • ① 직원의 팀번호는 팀을 가리키는 FK이고, 아직 팀이 정해지지 않은 직원도 있습니다. 직원을 모두 보며 팀 이름을 붙이려면 직원 쪽 행을 모두 남기는 외부 조인을 씁니다.
  • ② 직원의 멘토번호는 같은 직원 테이블의 직원번호를 가리킵니다. 직원마다 멘토 이름을 붙이려면 직원 테이블을 FROM에 한 번만 쓰고 멘토번호 = 직원번호로 잇습니다.
  • ③ 단지 → 동 → 호가 식별 관계로 이어져 호의 주식별자가 '단지번호 + 동번호 + 호번호'입니다. 3단지의 호를 모두 찾으려면 동을 조인한 뒤 동의 단지번호로 걸러야 합니다.
  • ④ 대여 한 건은 개인회원과 단체회원 가운데 한쪽하고만 관계를 맺습니다. 이런 관계는 순환 관계라 하고, 셀프 조인으로 잇습니다.
정답

① — 팀이 없는 직원은 팀번호가 NULL이라 내부 조인에서 빠집니다. 직원 입장에서 상대인 팀이 선택이니, 짝 없는 직원 행도 남기는 외부 조인을 씁니다.

  • ② 같은 테이블을 한 번만 쓰면 직원 쪽과 멘토 쪽을 가를 수 없습니다. 셀프 조인은 직원 테이블을 직원 쪽·멘토 쪽 두 별칭으로 두 번 쓰고, 직원 쪽의 멘토번호를 멘토 쪽의 직원번호와 잇습니다.
  • ③ 손자가 할아버지의 식별자를 물려받은 것을 놓쳤습니다. 식별 관계라 호의 주식별자에 단지번호가 들어 있어, 동을 조인하지 않고 호의 단지번호로 바로 거릅니다.
  • ④ 상호배타적 관계를 순환 관계와 섞었습니다. 여러 부모 가운데 하나하고만 맺는 관계는 상호배타적 관계이고, 순환 관계는 FK가 자기 테이블의 PK를 가리키는 관계입니다.

다시 볼 레슨: 관계와 조인

문제 5

Oracle에서 주문 7과 주문상품을 넣다가 셋째 INSERT에서 오류가 났습니다. 그대로 COMMIT하고 이어서 ROLLBACK했습니다. 마지막 SELECT의 결과로 알맞은 것을 고르세요.

CREATE TABLE ORDERS (
  ORDNO NUMBER PRIMARY KEY,
  EMPNO NUMBER NOT NULL
    REFERENCES EMP (EMPNO)
);
CREATE TABLE ORDER_ITEM (
  ORDNO NUMBER NOT NULL
    REFERENCES ORDERS (ORDNO),
  PRODNO NUMBER NOT NULL,
  QTY NUMBER NOT NULL,
  PRIMARY KEY (ORDNO, PRODNO)
);
INSERT INTO ORDERS
VALUES (7, 1004);
INSERT INTO ORDER_ITEM
VALUES (7, 101, 2);
INSERT INTO ORDER_ITEM
VALUES (7, 102, NULL);
COMMIT;
ROLLBACK;
SELECT O.ORDNO,
  I.PRODNO,
  I.QTY
FROM ORDERS O
  LEFT OUTER JOIN ORDER_ITEM I
  ON I.ORDNO = O.ORDNO
ORDER BY I.PRODNO;
  • ① 행이 없습니다.
  • ② 주문 7의 상품 101(수량 2) 한 행이 나옵니다.
  • ③ 주문 7의 상품 101과 102 두 행이 나옵니다.
  • ④ 주문 7만 남고 상품 칸이 NULL인 한 행이 나옵니다.
정답

② — Oracle은 오류가 난 셋째 INSERT(QTY가 NULL)만 취소하고 앞의 두 INSERT는 남겨 둡니다. COMMIT이 그 둘을 확정해서, 뒤의 ROLLBACK은 되돌릴 것이 없습니다. 상품이 일부만 저장되지 않게 하려면 오류가 났을 때 COMMIT 대신 ROLLBACK으로 주문 7까지 모두 취소해야 합니다.

  • ① 오류가 나면 앞에서 넣은 주문 7과 상품 101까지 함께 취소된다고 봤습니다. Oracle은 오류가 난 문장 하나만 취소하고, 남은 두 행은 COMMIT으로 확정됩니다.
  • ③ 오류가 난 INSERT도 들어갔다고 봤습니다. QTY는 NOT NULL이라 수량을 비운 상품 102는 막혀(ORA-01400) 저장되지 않습니다.
  • ④ 오류가 주문상품 INSERT를 모두 취소했다고 봤습니다. 취소되는 것은 오류가 난 문장 하나라 상품 101이 남고, 짝이 있으니 외부 조인이라도 상품 칸이 NULL로 채워지지 않습니다.
ORDNOPRODNOQTY
71012

다시 볼 레슨: 모델이 표현하는 트랜잭션

문제 6

온라인 서점의 모델에 다음 두 관계가 있습니다. 이 모델대로 데이터를 넣고 지우는 방법으로 옳지 않은 것을 고르세요.

(가) 주문서와 주문줄은 서로 필수입니다. 주문서마다 주문줄이 하나 이상 있고, 주문줄은 꼭 한 주문서에 속합니다. 주문줄의 주문번호는 주문서를 가리키는 FK이며 NOT NULL입니다.

(나) 출판사와 도서는 양쪽이 모두 선택입니다. 도서를 아직 내지 않은 출판사도, 출판사 없이 나온 도서도 있습니다.

  • ① 주문서 한 건과 그 주문줄은 한 트랜잭션으로 넣고, 그 안에서도 주문서를 먼저 넣습니다.
  • ② 도서를 아직 내지 않은 새 출판사만 먼저 넣고 COMMIT해도 모델의 규칙에 어긋나지 않습니다.
  • ③ 주문서를 지울 때는 주문줄을 먼저, 주문서를 나중에 지우고, 두 테이블에서 지우는 일을 한 트랜잭션으로 처리합니다.
  • ④ '주문서마다 주문줄이 하나 이상 있다'는 규칙은 주문줄의 FK와 NOT NULL 제약이 지켜 줍니다.
정답

④ — 주문줄의 FK와 NOT NULL이 지키는 것은 '주문줄은 꼭 한 주문서에 속한다'입니다. '주문서마다 주문줄이 하나 이상'은 기본 제약 조건으로 막지 못해, 주문줄 없는 주문서만 넣고 COMMIT해도 오류 없이 들어갑니다. 이 규칙은 주문서와 주문줄을 한 트랜잭션으로 넣는 쪽이 지킵니다.

  • ① 맞습니다. 서로 필수라 한쪽만 저장된 채 남지 않게 한 묶음으로 넣고, 주문줄의 FK가 주문서를 가리키므로 묶음 안에서도 주문서가 먼저입니다.
  • ② 맞습니다. 출판사 쪽에서 도서는 선택이라, 도서가 하나도 없는 출판사도 올바른 데이터입니다.
  • ③ 맞습니다. 주문서를 먼저 지우면 주문줄이 아직 가리키고 있어 막히고, 주문줄만 지우고 멈추면 주문줄 없는 주문서가 남습니다. 그래서 차례는 자식 먼저, 묶음은 하나입니다.

다시 볼 레슨: 모델이 표현하는 트랜잭션

문제 7

수당 표 EXTRA에는 식대 MEAL과 출장비 TRIP이 있고, 몇 칸이 NULL입니다.

EMPNOMEALTRIP
100110NULL
1002205
1003NULL15
10043010

다음 쿼리의 결과 A, B, C로 알맞은 것을 고르세요.

CREATE TABLE EXTRA (
  EMPNO NUMBER PRIMARY KEY,
  MEAL NUMBER,
  TRIP NUMBER
);
INSERT INTO EXTRA
VALUES (1001, 10, NULL);
INSERT INTO EXTRA
VALUES (1002, 20, 5);
INSERT INTO EXTRA
VALUES (1003, NULL, 15);
INSERT INTO EXTRA
VALUES (1004, 30, 10);
SELECT SUM(MEAL + TRIP) AS A,
  SUM(MEAL) + SUM(TRIP) AS B,
  COUNT(MEAL) AS C
FROM EXTRA;
  • ① A 65, B 90, C 3
  • ② A 90, B 90, C 4
  • ③ A 65, B 65, C 3
  • ④ A 90, B 65, C 4
정답

① — MEAL + TRIP은 한쪽이 NULL인 1001·1003에서 NULL이 되어 SUM에서 빠지므로, A는 25 + 40 = 65입니다. 열마다 따로 더한 B는 60 + 30 = 90입니다. COUNT(MEAL)은 NULL을 빼고 세어 3입니다.

  • ② NULL을 0처럼 봤습니다. 한쪽이 NULL인 행은 MEAL + TRIP이 통째로 NULL이라 A의 합에서 빠지고, COUNT(MEAL)도 MEAL이 NULL인 1003을 빼고 세어 3입니다.
  • ③ B도 NULL이 있는 행을 통째로 뺀다고 봤습니다. SUM(MEAL)과 SUM(TRIP)은 열마다 NULL인 칸만 건너뛰므로 60과 30을 더한 90입니다.
  • ④ A와 B를 거꾸로 봤습니다. 행마다 먼저 더하는 A에서 NULL이 섞인 두 행이 빠져 65이고, 열마다 따로 더하는 B가 90입니다. COUNT(MEAL)은 행이 아니라 값을 세어 3입니다.
ABC
65903

다시 볼 레슨: NULL 속성

문제 8

Oracle에서 다음을 실행했을 때 결과 A, B로 알맞은 것을 고르세요.

INSERT INTO DEPT
VALUES (80, '감사', '');
SELECT COUNT(*) AS A,
  COUNT(LOC) AS B
FROM DEPT;
  • ① A 4, B 4
  • ② A 4, B 5
  • ③ A 5, B 4
  • ④ A 5, B 5
정답

③ — Oracle은 길이가 0인 문자열 ''을 NULL로 다뤄, 80번 부서의 LOC가 NULL이 됩니다. COUNT(*)는 5행을 모두 세고, COUNT(LOC)는 NULL을 빼고 세어 4입니다.

  • ① 빈 문자열을 넣는 INSERT가 막힌다고 봤습니다. LOC에는 NOT NULL이 없어 NULL이 되어도 행은 들어가고, DEPT는 5행이 됩니다.
  • ② 두 함수를 거꾸로 봤습니다. 칸이 아니라 행을 세는 COUNT(*)가 5이고, NULL이 된 LOC를 건너뛰는 COUNT(LOC)가 4입니다.
  • ④ SQL Server처럼 ''을 값으로 봤습니다. Oracle에서는 ''이 NULL이라 COUNT(LOC)에서 빠집니다.
AB
54

다시 볼 레슨: NULL 속성

문제 9

캠핑장의 예약 엔터티를 설계합니다. 예약 한 건은 자리 하나의 하룻밤이라, '자리번호 + 숙박일'이면 예약이 가려집니다. 시스템이 예약마다 1, 2, 3을 매기는 예약ID를 둘 수도 있습니다. 식별자를 판단한 것으로 옳은 것을 고르세요.

  • ① 캠핑장이 자리마다 붙여 예약 안내 문자에도 적는 자리번호는, 번호이므로 인조식별자입니다.
  • ② 예약ID를 주식별자로 둔다면, 같은 자리의 같은 날 예약이 두 번 들어오지 않게 자리번호·숙박일에 UNIQUE를 겁니다.
  • ③ 예약ID를 주식별자로 두면, 같은 자리의 같은 날 예약이 두 번 들어와도 기본키가 그 둘째 예약을 막아 줍니다.
  • ④ 자리 엔터티의 본질식별자인 자리번호가 속성 하나이고 바뀌지 않아도, 인조식별자 자리ID를 먼저 둡니다.
정답

② — 예약ID는 행마다 새 값이라, 같은 예약이 다시 들어와도 기본키에 걸리지 않습니다. 그래서 인조식별자를 주식별자로 두면 본질식별자인 자리번호·숙박일에 UNIQUE를 걸어 중복을 막습니다.

  • ① 번호라서 인조식별자로 봤습니다. 자리번호는 캠핑장이 업무에서 붙여 쓰는 번호라 본질식별자이고, 인조식별자는 업무에 없던 번호를 시스템이 행 구분에만 매긴 것입니다.
  • ③ 인조식별자가 중복도 막는다고 봤습니다. 새 예약 행은 새 예약ID를 받아 기본키에 걸리지 않습니다. 같은 업무 데이터를 막지 못하는 것이 인조식별자의 단점입니다.
  • ④ 인조식별자를 늘 먼저 둔다고 봤습니다. 본질식별자를 먼저 찾아 속성이 적고 바뀌지 않으면 그대로 쓰고, 너무 길거나 복잡할 때만 인조식별자를 둡니다.

다시 볼 레슨: 본질식별자와 인조식별자

문제 10

영화관 예매 표 TICKET은 인조식별자 TICKET_ID를 기본키로 두고, 본질식별자 속성 SHOWNO(상영번호)·SEATNO(좌석번호)에 UNIQUE를 걸었습니다. CUST는 예매한 사람입니다. 네 INSERT를 차례로 실행한 뒤 남는 행 수로 알맞은 것을 고르세요.

CREATE TABLE TICKET (
  TICKET_ID NUMBER
    GENERATED ALWAYS
    AS IDENTITY PRIMARY KEY,
  SHOWNO NUMBER NOT NULL,
  SEATNO VARCHAR2(3) NOT NULL,
  CUST VARCHAR2(10) NOT NULL,
  UNIQUE (SHOWNO, SEATNO)
);
INSERT INTO TICKET
  (SHOWNO, SEATNO, CUST)
VALUES (7, 'A1', '나래');
INSERT INTO TICKET
  (SHOWNO, SEATNO, CUST)
VALUES (7, 'A2', '보미');
INSERT INTO TICKET
  (SHOWNO, SEATNO, CUST)
VALUES (8, 'A1', '시우');
INSERT INTO TICKET
  (SHOWNO, SEATNO, CUST)
VALUES (7, 'A1', '다온');
SELECT SHOWNO,
  SEATNO,
  CUST
FROM TICKET
ORDER BY SHOWNO, SEATNO;
  • ① 1행
  • ② 2행
  • ③ 3행
  • ④ 4행
정답

③ — UNIQUE (SHOWNO, SEATNO)⁠는 두 열 값의 조합이 같을 때만 막습니다. 넷째 INSERT는 예매한 사람이 달라도 이미 있는 (7, 'A1') 조합이라 막히고(ORA-00001), 나머지 셋은 조합이 서로 달라 들어갑니다.

  • ① 두 열에 UNIQUE를 하나씩 건 것처럼 봤습니다. 상영번호만 같은 (7, 'A2')나 좌석번호만 같은 (8, 'A1')은 조합이 달라 들어갑니다.
  • ② 좌석번호가 같으면 막힌다고 봤습니다. 조합을 보므로, 상영번호가 다른 (8, 'A1')은 같은 좌석이라도 들어갑니다.
  • ④ 예매한 사람이 다르니 다른 행이라고 봤습니다. UNIQUE는 CUST를 보지 않아 넷째 (7, 'A1')을 막습니다. 기본키 TICKET_ID만 있었다면 넷 다 들어갔을 것입니다.
SHOWNOSEATNOCUST
7A1나래
7A2보미
8A1시우

다시 볼 레슨: 본질식별자와 인조식별자

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

SQLD 목차로