[SQLD] SQL 개발자 기출유형 모의고사 3회
SQLD SQL 개발자 실전 모의고사

SQL 개발자 기출유형 모의고사 3회

총 50문항 · 객관식 · A4 약 1장

1 / 1
A4정답 포함
SQLD SQL 개발자 실전 모의고사모두CBT · moducbt.com

SQL 개발자 기출유형 모의고사 3회

총 50문항 · 객관식 · 정답·해설 포함
문제 1
ANSI-SPARC 3단계 스키마 구조에 대한 설명으로 옳은 것은?
1내부 스키마는 사용자별 뷰를 정의한다
2개념 스키마는 물리 저장 구조를 정의한다
3외부 스키마는 사용자별 관점을 정의한다
4논리적 독립성은 내부 스키마와 관련된다
정답 및 해설

외부 스키마는 사용자나 응용 프로그램마다 필요한 데이터를 바라보는 관점을 정의한 것이다.

개념 스키마는 조직 전체의 통합 구조, 내부 스키마는 물리 저장 구조를 정의하며 이 층 분리로 데이터 독립성이 확보된다.

  • 물리 저장 구조(레코드 형식·인덱스)를 정의하는 것은 내부 스키마다.
  • 사용자별 뷰를 정의하는 것은 외부 스키마다.
  • 논리적 독립성은 개념 스키마가 바뀌어도 외부 스키마가 유지되는 성질이다.
문제 2
엔터티 이름을 정하는 원칙으로 적절하지 않은 것은?
1약어 사용은 가능한 한 피한다
2업무 영역이 다르면 같은 이름을 쓴다
3현업에서 쓰는 업무 용어로 이름을 짓는다
4가급적 단수 명사로 이름을 붙인다
정답 및 해설

엔터티 이름은 모델 전체에서 유일해야 하므로 업무 영역이 달라도 같은 이름을 다시 쓰지 않는다.

그 밖에 현업 업무 용어 사용, 약어 지양, 단수 명사, 생성 의미대로 이름 붙이기가 명명 원칙에 해당한다.

  • 현업 업무 용어 사용은 명명 원칙에 맞는다.
  • 약어를 피하는 것은 명명 원칙에 맞는다.
  • 단수 명사 사용은 명명 원칙에 맞는다.
문제 3
속성과 도메인(Domain)에 대한 설명으로 가장 적절한 것은?
1속성명에 약어를 붙여 의미를 구분한다
2한 속성에 여러 값을 묶어 저장한다
3한 속성은 여러 도메인을 가질 수 있다
4도메인은 속성의 허용 값 범위를 정한다
정답 및 해설

도메인은 각 속성이 가질 수 있는 값의 범위(데이터 타입·길이·허용 값)를 정한 것이다.

속성 하나는 도메인 하나에 속하고, 한 인스턴스의 속성에는 하나의 값만 저장하는 것이 원칙이다.

  • 속성은 하나의 도메인만 가진다.
  • 속성명은 약어를 피하고 업무에서 쓰는 용어로 짓는다.
  • 여러 값을 한 속성에 묶으면 1정규형을 위반한다.
문제 4
다음 ERD에 대한 설명으로 적절하지 않은 것은?

그림

1부서와 직원은 존재에 의한 관계다
2직원 간 관리 관계는 자기참조 관계다
3직원과 매출은 행위에 의한 관계다
4자기참조는 다른 두 엔터티 간 관계다
정답 및 해설

자기참조(재귀) 관계는 같은 엔터티의 인스턴스끼리 맺는 관계로, 직원의 관리자번호가 같은 직원 엔터티의 직원번호를 참조하는 경우다.

소속처럼 상태로 연결되면 존재에 의한 관계, 등록처럼 업무 행위로 연결되면 행위에 의한 관계다.

  • 직원이 부서에 소속되는 것은 존재에 의한 관계다.
  • 직원이 매출을 등록하는 것은 행위에 의한 관계다.
  • 관리자번호로 직원끼리 이어지는 것은 자기참조 관계다.
문제 5
두 엔터티의 관계를 비식별자 관계(점선)로 설정하는 것이 적절한 경우는?
1부모 키를 자식 PK로 상속해야 할 때
2자식이 부모와 생명주기를 함께할 때
3자식 식별에 부모 키가 꼭 필요할 때
4부모 없이도 자식 인스턴스가 생길 때
정답 및 해설

부모 없이도 자식이 생성될 수 있거나 생명주기가 따로 움직이면 비식별자 관계로 두고 부모 키를 일반 속성(FK)으로만 받는다.

반대로 부모에 강하게 종속되어 부모 키가 자식 식별에 필요하면 식별자 관계(실선)로 PK에 포함한다.

  • 생명주기를 함께하는 강한 종속은 식별자 관계 기준이다.
  • 자식 식별에 부모 키가 필요하면 식별자 관계다.
  • 부모 키를 PK로 상속하는 것이 곧 식별자 관계다.
문제 6
매출 엔터티 한 곳에 직원명·지점명까지 담던 모델을 정규화하여 다음 ERD로 분리하였다. 이에 대한 설명으로 가장 적절한 것은?

그림

1정규화로 조회 성능은 항상 좋아진다
2새 지점은 직원이 있어야 등록할 수 있다
3지점명을 보려면 매출에서 두 번 조인한다
4지점명 변경 시 매출 행도 함께 고친다
정답 및 해설

매출에는 직원번호만 있으므로 지점명을 얻으려면 매출→직원→지점으로 조인이 두 번 필요하다.

정규화는 수정·입력 이상을 없애지만 흩어진 데이터를 다시 모으는 조인이 늘어 조회 성능은 떨어질 수 있다.

  • 지점명은 지점 엔터티 한 행만 고치면 되므로 매출 수정이 필요 없다.
  • 조인이 늘어 조회가 느려질 수 있으므로 항상 좋아지지 않는다.
  • 지점이 독립 엔터티라 직원 없이도 등록할 수 있다.
문제 7
다음 ERD에서 각 게시글의 제목과 그 원글(상위 게시글)의 제목을 한 행으로 조회하는 방법으로 가장 적절한 것은?

그림

1게시글에 별칭 둘을 주어 셀프 조인한다
2원글 제목을 회원 엔터티로 옮긴다
3상위글번호에 UNIQUE 제약을 추가한다
4게시글과 회원을 카티션 곱으로 잇는다
정답 및 해설

상위글번호가 같은 게시글 엔터티의 글번호를 참조하는 계층(자기참조) 관계이므로 한 테이블을 두 번 읽는 셀프 조인으로 조회한다.

예컨대 자식 A 와 부모 B 두 별칭을 두고 A.상위글번호 = B.글번호 로 연결하며, 원글이 없는 글까지 보려면 OUTER JOIN 을 쓴다.

  • 카티션 곱은 모든 조합을 만들 뿐 원글을 찾지 못한다.
  • 원글 하나에 답글이 여럿이므로 UNIQUE 제약은 맞지 않는다.
  • 원글 제목은 게시글 속성이라 회원으로 옮길 이유가 없다.
문제 8
업무 규칙상 배송 1건은 배송품목 1건 이상과 함께 하나의 트랜잭션으로 등록되어야 한다. 다음 데이터와 모델에 대한 설명으로 옳은 것은?

[배송]

배송번호회원번호등록일
B01M72026-04-01
B02M32026-04-01
B03M72026-04-02

[배송품목]

배송번호품목순번품목명수량
B011머그컵2
B012텀블러1
B021우산1
1배송품목은 배송과 따로 커밋해도 된다
2B03은 품목 없이 등록돼 규칙을 어겼다
3배송 쪽의 품목 참여는 선택으로 그린다
4B01은 품목이 두 건이라 규칙을 어겼다
정답 및 해설

B03 은 배송품목이 한 건도 없으므로 배송과 품목을 하나의 트랜잭션으로 함께 등록하라는 규칙을 어긴 데이터다.

이런 업무 규칙은 모델에서 배송 쪽의 배송품목 관계를 필수 참여로 표현하고, 두 INSERT 를 한 단위로 커밋해 보장한다.

  • 품목 1건 이상이면 되므로 두 건인 B01 은 규칙에 맞다.
  • 따로 커밋하면 품목 없는 배송이 생길 수 있어 규칙을 깬다.
  • 반드시 함께 생기는 관계는 선택이 아니라 필수로 그린다.
문제 9
NULL에 대한 설명으로 적절하지 않은 것은?
1Oracle은 ''를 NULL로 처리해 저장한다
2NULL과의 산술 연산은 NULL이다
3SQL Server는 ''를 NULL로 본다
4NULL은 0이나 공백과 다른 값이다
정답 및 해설

SQL Server는 빈 문자열('')을 길이 0인 문자열로 저장하므로 NULL 과 구분하며, 빈 문자열을 NULL 로 바꾸는 것은 Oracle의 동작이다.

NULL 은 값이 없거나 알 수 없음을 뜻하므로 숫자 0, 공백 문자와 다르다.

  • NULL 은 0·공백과 다른 미지의 값이다.
  • NULL 이 섞인 사칙 연산 결과는 NULL 이다.
  • Oracle 은 빈 문자열을 입력하면 NULL 로 저장한다.
문제 10
인조식별자에 대한 설명으로 적절하지 않은 것은?
1시퀀스 등으로 순번을 자동 부여한다
2같은 데이터의 중복 입력을 막아 준다
3복잡한 복합 식별자를 단순하게 바꾼다
4불필요한 인덱스가 늘어날 수 있다
정답 및 해설

인조식별자는 값이 매번 새로 매겨지므로 같은 업무 데이터가 두 번 들어와도 막지 못해 중복 데이터가 생길 수 있다.

그래서 본질식별자 열에 유니크 인덱스를 따로 두는 경우가 많고, 그만큼 인덱스가 늘어나는 단점이 있다.

  • 시퀀스·IDENTITY 로 자동 부여하는 것이 인조식별자의 특징이다.
  • 여러 속성으로 된 복합 식별자를 단일 열로 단순화한다.
  • 본질식별자 중복 방지용 인덱스가 추가로 필요해진다.
문제 11
다음 상황이 보여 주는 트랜잭션의 특성은?

배송 상태를 '완료'로 바꾸고 COMMIT 한 직후 DB 서버가 비정상 종료되었다. 재기동한 뒤 조회해도 해당 배송은 '완료' 상태로 남아 있었다.

1일관성(Consistency)
2고립성(Isolation)
3원자성(Atomicity)
4지속성(Durability)
정답 및 해설

커밋이 완료된 변경은 장애가 나도 사라지지 않고 영구히 반영되어야 한다는 특성이 지속성(Durability)이다.

DBMS 는 커밋 시점의 로그를 디스크에 남겨 재기동할 때 커밋된 변경을 복구한다.

  • 원자성은 트랜잭션 연산이 전부 반영되거나 전부 취소되는 성질이다.
  • 일관성은 실행 전후 DB 가 무결성 제약을 만족하는 성질이다.
  • 고립성은 동시에 실행되는 트랜잭션끼리 간섭하지 않는 성질이다.
문제 12
다음 SQL의 실행 결과로 옳은 것은?

[직원]

직원번호기본급수당
130050
2250(null)
328030
4(null)40
53200
SELECT SUM(기본급 + 수당) AS S1,
       SUM(기본급) + SUM(수당) AS S2,
       COUNT(기본급 + 수당) AS CNT
  FROM 직원;
1
S1S2CNT
9809803
2
S1S2CNT
127012705
3
S1S2CNT
98012703
4
S1S2CNT
98012705
정답 및 해설

기본급 + 수당은 행마다 먼저 계산되어 어느 한쪽이 NULL 인 직원번호 2·4 는 NULL이 되고, 집계 함수는 이 NULL 을 빼고 계산해 S1=350+310+320=980, CNT=3 이다.

반면 S2 는 열별로 NULL 을 제외해 먼저 합한 뒤 더하므로 1150+120=1270이 된다.

  • NULL 을 0 으로 보면 S1 이 1270, CNT 가 5 가 되지만 실제와 다르다.
  • COUNT(식)은 식이 NULL 인 행을 세지 않아 5 가 아니다.
  • S2 는 열마다 따로 합하므로 S1 과 같은 980 이 아니다.
문제 13
SELECT 문에 대한 설명으로 옳은 것은?
1열 별칭은 WHERE 절에서 쓸 수 없다
2DISTINCT는 첫 번째 열에만 적용된다
3덧셈이 곱셈보다 먼저 계산된다
4Oracle 연결 연산자는 + 기호다
정답 및 해설

WHERE 절은 SELECT 절보다 먼저 처리되므로 SELECT 절에서 붙인 열 별칭을 WHERE 절에서 참조할 수 없고, ORDER BY 절에서는 쓸 수 있다.

논리 실행 순서는 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 이다.

  • DISTINCT 는 SELECT 목록의 모든 열 조합에 적용된다.
  • 곱셈·나눗셈이 덧셈·뺄셈보다 먼저 계산된다.
  • Oracle 의 연결 연산자는 || 이고 + 는 SQL Server 방식이다.
문제 14
Oracle 기준으로, 출고일(DATE, 시각 포함)이 2026년 3월인 배송만 다음 결과처럼 조회하려 한다. ( ㉠ )에 들어갈 조건으로 옳은 것은? (세션 기본 날짜 형식은 RR/MM/DD)

[배송]

배송번호출고일기사명
D012026-02-28 23:40정우
D022026-03-01 09:00서진
D032026-03-15 13:20정우
D042026-03-31 18:05하린
D052026-04-01 00:00서진
D062025-03-20 10:30하린
SELECT 배송번호, TO_CHAR(출고일, 'YYYY-MM-DD HH24:MI') AS 출고
  FROM 배송
 WHERE ( ㉠ )
 ORDER BY 배송번호;

[원하는 결과]

배송번호출고
D022026-03-01 09:00
D032026-03-15 13:20
D042026-03-31 18:05
1TO_CHAR(출고일, 'YYYYMM') = '202603'
2EXTRACT(MONTH FROM 출고일) = 3
3SUBSTR(출고일, 1, 6) = '202603'
4TRUNC(출고일) = TO_DATE('202603', 'YYYYMM')
정답 및 해설

TO_CHAR(출고일, 'YYYYMM') 은 시각과 무관하게 연월 문자열만 만들어 비교하므로 3월 1일 09시부터 31일 18시까지의 D02·D03·D04 만 남는다.

DATE 를 문자열로 비교할 때는 형식 모델을 명시해야 하며, 형식 없이 문자 함수에 넣으면 세션 기본 형식으로 암시적 형변환된다.

  • TO_DATE('202603','YYYYMM') 은 3월 1일 0시라 TRUNC 결과가 같은 D02 만 나온다.
  • 월만 비교하면 2025년 3월의 D06 까지 포함된다.
  • 기본 형식으로 바뀐 '26/03/15' 앞 6자는 '202603' 과 같아질 수 없다.
문제 15
Oracle 기준으로 다음 SQL을 실행한 결과로 옳은 것은?

[회원]

회원번호등급가입일
M01G2026-01-05
M02S2026-01-09
M03(null)2026-02-11
M04G2026-02-20
M05B2026-03-02
SELECT 회원번호,
       DECODE(등급, NULL, 'N', 'G', 'Y', 'X') AS D,
       CASE 등급 WHEN NULL THEN 'N'
                 WHEN 'G'  THEN 'Y'
                 ELSE 'X' END AS C
  FROM 회원
 ORDER BY 회원번호;
1
회원번호DC
M01YY
M02XX
M03NX
M04YY
M05XX
2
회원번호DC
M01YY
M02XX
M03XX
M04YY
M05XX
3
회원번호DC
M01YY
M02XX
M03XN
M04YY
M05XX
4
회원번호DC
M01YY
M02XX
M03NN
M04YY
M05XX
정답 및 해설

DECODE 는 비교 대상과 검색값이 모두 NULL 이면 같다고 보므로 M03 의 D 는 'N' 이 되지만, 단순 CASE 의 WHEN NULL 은 등급 = NULL 비교라 결과가 UNKNOWN 이어서 ELSE 의 'X' 가 나온다.

나머지 회원은 G 면 'Y', S·B 면 'X' 로 두 열이 같다.

  • CASE 도 NULL 을 잡는다고 본 결과로 M03 의 C 가 틀렸다.
  • DECODE 도 NULL 을 못 잡는다고 본 결과로 M03 의 D 가 틀렸다.
  • 두 함수의 NULL 처리를 서로 뒤바꾼 결과다.
문제 16
Oracle 기준으로 다음 SQL을 실행한 결과로 옳은 것은?

[매출]

매출번호지점코드금액
S1B101250
S2B101349
S3B20-1250
S4B2080
SELECT 매출번호,
       ROUND(금액, -2) AS R,
       TRUNC(금액, -2) AS T,
       MOD(금액, 300)  AS M
  FROM 매출
 ORDER BY 매출번호;
1
매출번호RTM
S11300120050
S213001300149
S3-1300-1200-50
S40080
2
매출번호RTM
S11300120050
S213001300149
S3-1300-1200-50
S4100080
3
매출번호RTM
S11300120050
S213001300149
S3-1300-1200250
S4100080
4
매출번호RTM
S11300120050
S213001300149
S3-1200-1200-50
S4100080
정답 및 해설

자릿수 -2 는 십의 자리에서 처리하므로 ROUND 는 -1250 을 절댓값 기준으로 올려 -1300, 80 을 100 으로 만들고, TRUNC 는 버리기만 해 -1200·0 이 된다.

MOD 는 나머지의 부호가 피제수를 따르므로 -1250 을 300 으로 나눈 나머지는 -50 이다.

  • -1250 의 반올림을 큰 쪽으로 올린 -1200 은 ROUND 결과가 아니다.
  • 나머지를 양수로 맞춘 250 은 Oracle MOD 결과가 아니다.
  • 80 은 십의 자리 8 이 5 이상이라 0 이 아니라 100 으로 반올림된다.
문제 17
다음 SQL의 결과로 출력되는 글번호를 모두 나열한 것은?

[게시글]

글번호분류조회수
101공지50
102질문120
103(null)30
104질문(null)
105자유80
106(null)150
107자유100
SELECT 글번호
  FROM 게시글
 WHERE NOT (분류 = '공지' OR 조회수 > 100)
 ORDER BY 글번호;
1103, 105, 107
2105, 107
3105
4104, 105, 107
정답 및 해설

괄호 안이 FALSE 인 105·107 만 NOT 을 거쳐 TRUE 가 되며, 103(분류 NULL)과 104(조회수 NULL)는 괄호 안이 UNKNOWN 이고 NOT UNKNOWN 도 UNKNOWN 이라 제외된다.

106 은 분류가 NULL 이어도 조회수 150 > 100 이 TRUE 라 OR 전체가 TRUE 가 되어 빠진다.

  • 107 의 조회수 100 은 100 초과가 아니므로 결과에 포함된다.
  • 분류가 NULL 인 103 을 FALSE 로 보면 잘못 포함된다.
  • 조회수가 NULL 인 104 도 UNKNOWN 이라 출력되지 않는다.
문제 18
[기준 조건]과 항상 같은 결과를 내는 후보 조건은?

[기준 조건]

WHERE NOT 지점 = 'A' AND 금액 >= 100 OR 직급 = '과장'

[후보 조건]

㉠ WHERE NOT (지점 = 'A' AND 금액 >= 100) OR 직급 = '과장'
㉡ WHERE (지점 <> 'A' AND 금액 >= 100) OR 직급 = '과장'
㉢ WHERE 지점 <> 'A' AND (금액 >= 100 OR 직급 = '과장')
㉣ WHERE NOT (지점 = 'A' AND 금액 >= 100 OR 직급 = '과장')
1㉡
2㉢
3㉠
4㉣
정답 및 해설

논리 연산자는 NOT → AND → OR 순으로 결합하므로 기준 조건은 ((NOT 지점 = 'A') AND 금액 >= 100) OR 직급 = '과장' 과 같고, NOT 지점 = 'A' 는 지점 <> 'A' 와 같다.

따라서 ㉡처럼 AND 묶음을 먼저 만든 뒤 OR 로 직급 조건을 더한 형태가 동일하다.

  • ㉠은 NOT 이 AND 묶음 전체에 걸려 의미가 달라진다.
  • ㉢은 OR 를 AND 보다 먼저 묶어 과장도 지점 A 면 빠진다.
  • ㉣은 NOT 이 조건 전체를 부정해 정반대에 가깝다.
문제 19
다음 SQL 중 실행 시 오류가 발생하는 것은?

[매출]

매출번호지점코드금액
1A300
2A250
3B90
4B400
5C120
㉠ SELECT 지점코드, SUM(금액)
     FROM 매출
    GROUP BY 지점코드
   HAVING COUNT(*) >= 2;

㉡ SELECT SUM(금액)
     FROM 매출
   HAVING SUM(금액) > 0;

㉢ SELECT 지점코드, SUM(금액)
     FROM 매출
    WHERE SUM(금액) > 500
    GROUP BY 지점코드;

㉣ SELECT 지점코드, COUNT(*)
     FROM 매출
    WHERE 금액 > 100
    GROUP BY 지점코드
   HAVING 지점코드 <> 'B';
1㉢
2㉡
3㉠
4㉣
정답 및 해설

WHERE 절은 그룹이 만들어지기 전에 행 단위로 처리되므로 집계 함수를 쓸 수 없고, 그룹 조건 SUM(금액) > 500 은 HAVING 절로 옮겨야 한다.

HAVING 에는 SELECT 목록에 없는 집계나 GROUP BY 열도 조건으로 쓸 수 있다.

  • ㉠은 SELECT 에 없는 COUNT(*) 를 HAVING 에 써도 정상 실행된다.
  • ㉡은 GROUP BY 없이 테이블 전체를 한 그룹으로 보고 HAVING 을 적용한다.
  • ㉣은 WHERE 로 행을 거른 뒤 GROUP BY 열 조건을 HAVING 에 둔 형태라 정상이다.
문제 20
다음 SQL의 결과 행 수는?

[배송]

배송번호지역상태
D1서울완료
D2서울완료
D3서울취소
D4부산완료
D5부산(null)
D6(null)완료
D7(null)대기
D8부산대기
D9서울대기
D10부산(null)
SELECT 지역, 상태, COUNT(*) AS 건수
  FROM 배송
 WHERE 상태 <> '취소'
 GROUP BY 지역, 상태
 ORDER BY 지역, 상태;
15
27
36
44
정답 및 해설

상태 <> '취소' 는 D3 뿐 아니라 상태가 NULL 인 D5·D10 도 UNKNOWN 이라 제외하고, 남은 7행을 (지역, 상태)로 묶으면 NULL 지역도 하나의 그룹이 된다.

그룹은 (서울,완료)·(서울,대기)·(부산,완료)·(부산,대기)·(NULL,완료)·(NULL,대기) 의 6개다.

  • 지역이 NULL 인 두 그룹을 빼면 4행이 되지만 GROUP BY 는 NULL 을 버리지 않는다.
  • NULL 지역을 한 그룹으로 합치면 5행이지만 상태가 달라 두 그룹이다.
  • 상태 NULL 행을 남겨 (부산,NULL) 그룹을 만들면 7행으로 잘못 센다.
문제 21
다음 SQL의 실행 결과로 옳은 것은?

[게시글]

글번호작성자추천수
1민호10
2수빈(null)
3민호20
4지안0
5수빈(null)
6지안30
SELECT COUNT(*)      AS A,
       COUNT(추천수) AS B,
       AVG(추천수)   AS C
  FROM 게시글;
1
ABC
6415
2
ABC
6610
3
ABC
4415
4
ABC
6410
정답 및 해설

COUNT(*) 는 NULL 여부와 관계없이 6행을 모두 세고, COUNT(추천수) 는 NULL 2건을 빼고 0 은 값이므로 포함해 4 이다.

AVG 도 NULL 을 뺀 4건으로 나누어 (10+20+0+30)/4 = 15 가 된다.

  • NULL 을 0 으로 보면 B=6, C=10 이 되지만 집계 함수는 NULL 을 제외한다.
  • COUNT(*) 는 NULL 이 있는 행도 세므로 4 가 아니다.
  • AVG 는 전체 행 수 6 이 아니라 NULL 아닌 4건으로 나눈다.
문제 22
다음 SQL의 실행 결과로 옳은 것은? (부서명은 가나다순으로 비교된다)

[직원]

직원번호부서급여
E1영업300
E2개발400
E3영업250
E4인사300
E5개발350
E6영업300
SELECT 직원번호, 부서 AS 소속, 급여
  FROM 직원
 ORDER BY 소속 DESC, 3, 1 DESC;
1
직원번호소속급여
E4인사300
E6영업300
E1영업300
E3영업250
E2개발400
E5개발350
2
직원번호소속급여
E5개발350
E2개발400
E3영업250
E6영업300
E1영업300
E4인사300
3
직원번호소속급여
E4인사300
E3영업250
E1영업300
E6영업300
E5개발350
E2개발400
4
직원번호소속급여
E4인사300
E3영업250
E6영업300
E1영업300
E5개발350
E2개발400
정답 및 해설

소속 내림차순이라 인사→영업→개발 순이고, 같은 부서 안에서는 열 번호 3(급여) 오름차순, 급여가 같으면 열 번호 1(직원번호) 내림차순으로 E6 이 E1 앞에 온다.

DESC 는 바로 앞의 정렬 키 하나에만 적용되고 방향을 생략한 키는 ASC 다.

  • E1 을 E6 앞에 둔 표는 마지막 키의 DESC 를 무시했다.
  • 급여까지 내림차순으로 정렬한 표는 DESC 가 모든 키에 걸린다고 오해했다.
  • 개발부터 시작한 표는 소속을 오름차순으로 정렬했다.
문제 23
다음 두 SQL ㉮, ㉯의 결과 행 수를 순서대로 나타낸 것은?

[지점]

지점코드지점명
B1강남
B2판교
B3수원

[직원]

직원번호지점코드
E1B1
E2B1
E3B2
E4(null)

[매출]

매출번호직원번호금액
S1E1100
S2E1200
S3E3150
S4E4300
S5E950
㉮ SELECT B.지점명, S.매출번호, S.금액
     FROM 지점 B, 직원 E, 매출 S
    WHERE E.직원번호 = S.직원번호;

㉯ SELECT B.지점명, S.매출번호, S.금액
     FROM 지점 B, 직원 E, 매출 S
    WHERE E.직원번호 = S.직원번호
      AND B.지점코드 = E.지점코드;
112, 3
212, 4
34, 3
415, 3
정답 및 해설

㉮는 직원·매출만 연결하고 지점과의 조건이 없어, 직원번호가 맞는 매출 4건(S1·S2·S3·S4)에 지점 3행이 카티션 곱으로 붙어 12행이다.

㉯처럼 테이블 3개에는 N-1=2개 조인 조건이 필요하며, 지점코드가 NULL 인 E4 의 S4 는 조인에서 빠져 3행이 된다.

  • 12와 4 는 지점코드 NULL 인 직원도 조인된다고 본 결과다.
  • 15 는 직원이 없는 S5 까지 곱한 수로, 직원과의 조건에서 이미 빠진다.
  • 4 는 빠진 조인 조건이 행을 늘리지 않는다고 본 결과다.
문제 24
다음 SQL의 실행 결과에 대한 설명으로 옳은 것은?

[직원]

직원번호직원명관리자번호
101윤서(null)
102도윤101
103하은101
104시우102
105지아(null)
SELECT A.직원명 AS 직원, B.직원명 AS 관리자
  FROM 직원 A
  JOIN 직원 B
    ON A.관리자번호 = B.직원번호
 ORDER BY A.직원번호;
1관리자가 없는 윤서도 직원 열에 나온다
2같은 테이블이라 별칭 없이 실행된다
3시우의 관리자로 윤서가 출력된다
4관리자 열에 윤서가 두 번 출력된다
정답 및 해설

A 를 직원, B 를 관리자로 보는 셀프 조인에서 관리자번호가 있는 도윤·하은·시우만 연결되어 3행이 나오며, 도윤·하은의 관리자가 모두 윤서다.

INNER JOIN 이므로 관리자번호가 NULL 인 윤서·지아는 직원 열에 나오지 않는다.

  • 윤서까지 보려면 LEFT OUTER JOIN 이 필요하다.
  • 한 번의 조인은 바로 위 한 단계만 이어 시우의 관리자는 도윤이다.
  • 같은 테이블을 두 번 쓰면 별칭이 없을 때 열을 구분할 수 없다.
문제 25
다음 결과를 얻기 위해 ( ㉠ )에 들어갈 구문으로 옳은 것은?

[부서]

부서코드부서명등록일
D1영업2026-01-02
D2개발2026-01-02
D3인사2026-02-01

[직원]

직원번호직원명부서코드등록일
201서아D12026-01-02
202준호D22026-03-05
203예린D12026-02-01
204태민D32026-02-01
205유나D22026-01-02
SELECT 직원번호, 부서코드, 부서명
  FROM 직원 ( ㉠ )
 ORDER BY 직원번호;

[원하는 결과]

직원번호부서코드부서명
201D1영업
202D2개발
203D1영업
204D3인사
205D2개발
1LEFT JOIN 부서 USING (등록일)
2JOIN 부서 USING (부서코드)
3JOIN 부서 ON 직원.부서코드 = 부서.부서코드
4NATURAL JOIN 부서
정답 및 해설

두 테이블에는 부서코드와 등록일이 함께 있으므로 부서코드 하나로만 조인하려면 USING (부서코드) 를 써야 하고, USING 열은 접두사 없이 SELECT 할 수 있다.

NATURAL JOIN 은 이름이 같은 열을 모두 조건으로 써 등록일까지 같은 201·204·205 의 3행만 남는다.

  • NATURAL JOIN 은 등록일이 다른 202·203 을 빠뜨린다.
  • 등록일로 조인하면 부서가 엉뚱하게 짝지어지고 부서코드도 모호해진다.
  • ON 절을 쓰면 접두사 없는 부서코드가 모호하다는 오류가 난다.
문제 26
Oracle 기준으로 다음 ㉠의 결과 행 수와 ㉡의 결과 값을 순서대로 나열한 것은?

[부서]

부서코드부서명
10기획
20영업
30개발
40인사

[직원]

사번이름부서코드급여
101김윤10450
102이솔10280
103박건20320
104최담20350
105정우30250
106조현30260
107한결(null)500

㉠

SELECT D.부서명, E.이름
  FROM 부서 D, 직원 E
 WHERE D.부서코드 = E.부서코드(+)
   AND E.급여(+) >= 300;

㉡

SELECT COUNT(*) FROM 부서 CROSS JOIN 직원;
13, 28
27, 24
35, 28
45, 24
정답 및 해설

㉠은 E.급여(+) >= 300 까지 (+) 가 붙어 LEFT OUTER JOIN 의 ON 조건이 되므로 5행이고, ㉡의 CROSS JOIN 은 4×7=28행이다.

㉠에서 기획은 김윤, 영업은 박건·최담과 결합하고, 급여 300 미만뿐인 개발과 직원이 없는 인사는 직원 열이 NULL 인 한 행씩 남는다.

  • 3행은 급여 조건을 WHERE 필터로 본 결과로, 개발·인사 행이 사라진다.
  • 7행은 급여 조건을 무시하고 부서코드만으로 외부 조인한 결과이다.
  • 24는 부서코드가 NULL 인 직원을 곱에서 뺀 값이나, CROSS JOIN 은 조건 없이 모든 행을 곱한다.
문제 27
지점을 왼쪽, 매출을 오른쪽 테이블로 두고 지점코드로 조인할 때, 설명으로 적절하지 않은 것은?

[지점]

지점코드지점명
A강남
B판교
C수원

[매출]

매출번호지점코드금액
1A100
2A200
3B150
4D80
1INNER JOIN 결과는 3행이다
2LEFT 조인에서 수원은 NULL 과 결합된다
3FULL OUTER JOIN 결과는 5행이다
4RIGHT 조인에서 D 매출 행은 빠진다
정답 및 해설

RIGHT OUTER JOIN 은 오른쪽 매출 테이블의 모든 행을 남기므로, 짝이 없는 지점코드 D 매출도 지점 열이 NULL 인 채 결과에 포함된다.

매칭 행은 A 두 건과 B 한 건이며, 각 외부 조인은 기준 쪽의 짝 없는 행을 NULL 과 결합해 덧붙인다.

  • INNER JOIN 은 양쪽에 모두 있는 A 두 건과 B 한 건으로 3행이다.
  • LEFT 조인은 지점이 기준이므로 매출이 없는 수원이 NULL 과 결합된다.
  • FULL OUTER JOIN 은 매칭 3행에 수원과 D 매출이 더해져 5행이다.
문제 28
서브쿼리(Subquery)의 동작으로 적절하지 않은 것은?
1다중행 결과를 = 로 비교하면 오류다
2HAVING 절에도 서브쿼리를 쓸 수 있다
3결과가 0행이면 반드시 오류가 발생한다
4EXISTS 는 행의 존재 여부만 따진다
정답 및 해설

서브쿼리 결과가 0행이어도 오류가 나지 않으며, 비교 대상이 없으니 조건을 만족하는 행이 없을 뿐이고 스칼라 서브쿼리는 NULL 로 처리된다.

오류는 단일행 연산자로 비교하는 서브쿼리가 두 행 이상을 돌려줄 때 생긴다.

  • EXISTS 는 서브쿼리의 SELECT 값과 무관하게 행이 하나라도 있으면 참이다.
  • = 같은 단일행 연산자에 여러 행이 오면 오류가 나므로 IN 등을 써야 한다.
  • HAVING 절에 서브쿼리를 두어 그룹 집계 결과와 비교할 수 있다.
문제 29
다음 ㉠과 ㉡의 결과 값을 순서대로 나열한 것은?

[회원]

회원번호이름
1서하
2민재
3유나
4태윤
5지오

[게시글]

글번호작성자번호제목
111가입 인사
121질문
133후기
14(null)탈퇴회원 글
154공지 문의

㉠

SELECT COUNT(*) FROM 회원
 WHERE 회원번호 NOT IN (SELECT 작성자번호 FROM 게시글);

㉡

SELECT COUNT(*) FROM 회원 M
 WHERE NOT EXISTS (SELECT 1 FROM 게시글 P
                    WHERE P.작성자번호 = M.회원번호);
10, 2
22, 2
32, 0
41, 2
정답 및 해설

㉠은 서브쿼리 결과에 NULL 이 섞여 NOT IN 비교가 어떤 회원에서도 참이 되지 못해 0이고, ㉡의 NOT EXISTS 는 NULL 과 무관하게 글이 없는 민재·지오를 세어 2이다.

x NOT IN (1, 3, NULL, 4) 는 x <> NULL 비교가 UNKNOWN 이므로 결과가 참이 될 수 없다.

  • ㉠을 2로 본 것은 NULL 행을 무시한다고 착각한 경우이다.
  • ㉡을 0으로 본 것은 NOT EXISTS 도 NULL 에 영향받는다고 본 경우이다.
  • ㉠을 1로 볼 근거는 없으며, NULL 하나가 전체 비교를 막는다.
문제 30
다음 [결과]를 얻기 위해 ㉠에 들어갈 연산자로 옳은 것은?

[직원]

사번이름부서급여
1강민영업300
2고은영업450
3문수개발500
4배진개발200
5신혜인사600
6오현인사300
SELECT 이름
  FROM 직원
 WHERE 급여 ( ㉠ ) (SELECT 급여 FROM 직원
                    WHERE 부서 = '영업')
 ORDER BY 이름;

[결과]

이름
문수
신혜
1= ANY
2> ALL
3NOT IN
4< ANY
정답 및 해설

영업 급여는 300과 450이고 결과는 이 둘보다 모두 큰 문수(500)·신혜(600)이므로 > ALL 이 맞다.

> ALL 은 서브쿼리의 최댓값을 넘는 행, < ANY 는 최댓값 미만인 행을 고른다.

  • < ANY 는 450 미만인 강민·배진·오현을 반환한다.
  • = ANY 는 IN 과 같아 급여가 300 또는 450 인 강민·고은·오현을 반환한다.
  • NOT IN 은 300·450 이 아닌 문수·배진·신혜를 반환한다.
문제 31
다음 SQL 의 결과 행 수와 마지막 행의 매출액을 순서대로 나열한 것은?

[본점실적]

직원명금액
김하늘300
이서진150
박구름90
최별200

[지점실적]

사원명실적
이서진150
정노을250
한빛80
SELECT 직원명 AS 이름, 금액 AS 매출액
  FROM 본점실적
 WHERE 금액 >= 100
UNION
SELECT 사원명, 실적
  FROM 지점실적
 ORDER BY 매출액 DESC;
16, 80
25, 80
36, 90
44, 150
정답 및 해설

WHERE 는 첫 SELECT 에만 걸려 박구름만 빠지고, UNION 이 중복된 이서진 150 을 한 번만 남겨 5행이며 마지막 행은 한빛 80이다.

ORDER BY 는 집합 연산 맨 끝에 한 번만 쓰며 전체 결과에 적용되고, 열 이름은 첫 SELECT 의 별칭을 따르므로 매출액으로 정렬할 수 있다.

  • 6행은 UNION ALL 처럼 중복 행을 남긴 경우이다.
  • 4행·150 은 WHERE 가 두 번째 SELECT 에도 걸린다고 본 경우이다.
  • 6행·90 은 WHERE 조건을 무시해 박구름까지 남긴 경우이다.
문제 32
집합 연산자(Set Operator)에 대한 설명으로 옳은 것은?
1INTERSECT 는 공통 행을 중복 포함해 반환한다
2UNION ALL 은 중복 행을 그대로 남긴다
3열 이름은 마지막 SELECT 기준으로 정해진다
4UNION ALL 은 정렬된 결과를 보장한다
정답 및 해설

UNION ALL 은 두 결과를 이어 붙이기만 하므로 중복 행이 그대로 남고, 행 수는 두 결과 행 수의 합과 같다.

중복 제거 과정이 없어 UNION 보다 빠르며, 결과 열 이름은 첫 번째 SELECT 를 따른다.

  • 결과 열 이름과 별칭은 마지막이 아니라 첫 SELECT 에서 정해진다.
  • 정렬이 필요하면 맨 끝에 ORDER BY 를 써야 하며 UNION ALL 은 순서를 보장하지 않는다.
  • INTERSECT 는 공통 행을 중복 없이 반환한다.
문제 33
다음 [결과]를 얻기 위해 ㉠에 들어갈 구문으로 옳은 것은?

[매출]

지점분기금액
강남1Q100
강남2Q150
판교1Q200
판교2Q120
수원1Q80
SELECT 지점, SUM(금액) AS 합계,
       GROUPING(지점) AS G
  FROM 매출
 GROUP BY ㉠
 ORDER BY G, 지점;

[결과]

지점합계G
강남2500
수원800
판교3200
(null)6501
1지점, 분기
2GROUPING SETS (지점, ())
3CUBE (지점, 분기)
4ROLLUP (지점, 분기)
정답 및 해설

결과는 분기 없이 지점별 합계 3행과 G 가 1인 총계 1행뿐이므로 GROUPING SETS (지점, ()) 가 맞다.

GROUPING 함수는 해당 열이 집계로 묶여 NULL 이 된 행에서 1, 그 외에는 0을 돌려주며, 괄호 빈 집합 () 이 총계를 만든다.

  • ROLLUP (지점, 분기) 는 지점·분기별 상세 5행과 지점 소계까지 더해 9행이 된다.
  • CUBE (지점, 분기) 는 여기에 분기별 소계까지 더해진다.
  • 지점, 분기 로 묶으면 상세 5행만 나오고 총계가 없다.
문제 34
다음 ㉠과 ㉡의 결과 행 수를 순서대로 나열한 것은?

[배송실적]

지역월건수
서울1월40
서울2월35
부산1월20
대구1월15
대구2월25

㉠

SELECT 지역, 월, SUM(건수) FROM 배송실적
 GROUP BY ROLLUP (지역, 월);

㉡

SELECT 지역, 월, SUM(건수) FROM 배송실적
 GROUP BY ROLLUP (월, 지역);
111, 11
28, 9
39, 9
49, 8
정답 및 해설

ROLLUP 은 (전체 인수) → (첫 인수) → () 순으로 집계하므로 ㉠은 상세 5 + 지역 소계 3 + 총계 1 = 9행, ㉡은 상세 5 + 월 소계 2 + 총계 1 = 8행이다.

ROLLUP 은 인수 순서가 바뀌면 소계 대상이 바뀌어 결과가 달라지며, 순서와 무관한 것은 CUBE 이다.

  • 8, 9 는 소계가 마지막 인수 기준이라고 거꾸로 본 경우이다.
  • 9, 9 는 인수 순서가 결과에 영향이 없다고 본 경우이다.
  • 11, 11 은 두 방향 소계를 모두 만드는 CUBE 의 결과이다.
문제 35
다음 SQL 의 결과 전체 행 수와, 그중 직급 열이 NULL 인 행 수를 순서대로 나열한 것은?

[직원]

사번부서직급급여
1영업과장400
2영업대리300
3영업(null)250
4개발과장450
5개발대리350
6개발대리330
7인사(null)280
SELECT 부서, 직급, SUM(급여) AS 합계
  FROM 직원
 GROUP BY ROLLUP (부서, 직급);
111, 5
210, 6
39, 4
410, 4
정답 및 해설

상세 그룹은 영업 3(과장·대리·NULL)·개발 2·인사 1 로 6행이고, 여기에 부서 소계 3행과 총계 1행이 더해져 10행이다.

직급이 NULL 인 행은 소계·총계 4행에 원래 직급이 NULL 인 상세 그룹 2행이 더해져 6행이며, 둘을 구별하려면 GROUPING 함수를 쓴다.

  • 10, 4 는 원래 NULL 인 상세 그룹을 소계로만 본 경우이다.
  • 9, 4 는 총계 1행과 원래 NULL 인 상세 그룹 2행을 함께 빠뜨린 수이다.
  • 11, 5 는 행 수와 NULL 행 수 모두 실제 10행·6행과 맞지 않는다.
문제 36
다음 [결과]를 얻기 위해 ㉠에 들어갈 함수로 옳은 것은?

[월매출]

월금액
1100
2300
3200
4400
5150
SELECT 월, 금액,
       ( ㉠ ) OVER (ORDER BY 월) AS 비교값
  FROM 월매출
 ORDER BY 월;

[결과]

월금액비교값
1100200
2300400
3200150
4400(null)
5150(null)
1FIRST_VALUE(금액)
2MAX(금액)
3LEAD(금액, 2)
4LAG(금액)
정답 및 해설

각 행의 비교값이 두 달 뒤 금액이고 마지막 두 행이 NULL 이므로 LEAD(금액, 2) 이다.

LEAD 는 정렬 기준으로 뒤쪽 n번째 행 값을 가져오며, 가져올 행이 없으면 기본값을 주지 않는 한 NULL 이다.

  • LAG(금액) 는 직전 달 값이라 첫 행이 NULL 이고 100·300·200·400 이 이어진다.
  • FIRST_VALUE(금액) 는 모든 행에 첫 달 금액 100 을 준다.
  • MAX(금액) 는 기본 프레임에서 누적 최댓값 100·300·300·400·400 을 준다.
문제 37
다음 SQL 결과에서 이름이 다솜인 행의 PR, NT 값을 순서대로 나열한 것은?

[영업사원]

이름매출
가온200
나래250
다솜300
라온350
마루400
SELECT 이름,
       PERCENT_RANK() OVER (ORDER BY 매출) AS PR,
       NTILE(2) OVER (ORDER BY 매출) AS NT
  FROM 영업사원
 ORDER BY 매출;
10.4, 2
20.6, 1
30.5, 2
40.5, 1
정답 및 해설

다솜은 5행 중 3위이므로 PERCENT_RANK 는 (3−1)/(5−1)=0.5 이고, NTILE(2) 는 3행·2행으로 나누어 다솜이 첫 그룹이라 1이다.

NTILE 은 행 수가 나누어떨어지지 않으면 앞 그룹부터 한 행씩 더 배정한다.

  • NT 를 2로 본 것은 나머지 행을 뒤 그룹에 준다고 착각한 경우이다.
  • 0.6 은 자신 이하 행 비율인 CUME_DIST 값(3/5)이다.
  • 0.4 는 분모를 행 수 5로 잡은 계산으로 PERCENT_RANK 식이 아니다.
문제 38
다음 ㉠의 결과 행 수, ㉡의 결과 행 수, ㉡에서 부서가 개발인 행의 합계 값을 순서대로 나열한 것은?

[직원]

사번부서급여
1영업300
2영업200
3개발400
4개발350
5개발240
6(null)280
7인사330

㉠

SELECT 부서, SUM(급여) AS 합계
  FROM 직원
 WHERE 급여 >= 250
 GROUP BY 부서;

㉡

SELECT 부서, SUM(급여) OVER (PARTITION BY 부서) AS 합계
  FROM 직원
 WHERE 급여 >= 250;
13, 5, 750
24, 5, 750
34, 7, 990
44, 5, 990
정답 및 해설

WHERE 로 급여 200·240 두 행이 빠진 뒤 ㉠은 영업·개발·인사·NULL 의 4개 그룹, ㉡은 남은 5행을 그대로 돌려주고 개발 행 합계는 400+350=750이다.

윈도우 함수는 WHERE·GROUP BY 처리 뒤에 계산되며 행을 줄이지 않고, 부서가 NULL 인 행도 하나의 그룹·파티션이 된다.

  • 3개 그룹은 부서가 NULL 인 행이 그룹에서 빠진다고 본 경우이다.
  • 7행·990 은 윈도우 함수가 WHERE 보다 먼저 계산된다고 본 경우이다.
  • 5행이면서 990 은 합계에만 WHERE 가 적용되지 않는다고 본 모순된 계산이다.
문제 39
다음 SQL 결과에서 회원명이 마로인 행의 R, DR, RN 값을 순서대로 나열한 것은?

[회원]

회원명포인트
가람90
나비85
다인85
라임80
마로80
바름80
사랑70
SELECT 회원명,
       RANK() OVER (ORDER BY 포인트 DESC) AS R,
       DENSE_RANK() OVER (ORDER BY 포인트 DESC) AS DR,
       ROW_NUMBER() OVER (ORDER BY 포인트 DESC, 회원명) AS RN
  FROM 회원
 ORDER BY RN;
14, 3, 4
24, 4, 5
34, 3, 5
45, 3, 5
정답 및 해설

마로(80)는 앞에 90·85·85 세 행이 있어 RANK 4, 서로 다른 값으로는 세 번째라 DENSE_RANK 3 이다.

ROW_NUMBER 는 포인트가 같으면 회원명 순으로 라임 다음 번호를 받아 5가 되며, 동점이어도 같은 번호를 주지 않는다.

  • DR 을 4로 본 것은 DENSE_RANK 도 동점 수만큼 건너뛴다고 착각한 경우이다.
  • R 을 5로 본 것은 RANK 와 ROW_NUMBER 를 혼동한 경우이다.
  • RN 을 4로 본 것은 보조 정렬인 회원명 순서를 무시한 경우이다.
문제 40
Oracle(12c 이상)에서 다음 SQL 을 실행한 결과로 옳은 것은?

[상품]

상품코드분류판매량
P1식품120
P2생활(null)
P3식품90
P4가전150
P5생활90
P6가전90
P7식품60
SELECT 상품코드, 판매량
  FROM (SELECT 상품코드, 판매량
          FROM 상품
         ORDER BY 판매량 DESC NULLS LAST
         FETCH FIRST 3 ROWS WITH TIES) T
 ORDER BY 판매량 DESC, 상품코드;
1
상품코드판매량
P390
P590
P690
P760
2
상품코드판매량
P2(null)
P4150
P1120
3
상품코드판매량
P4150
P1120
P390
P590
P690
4
상품코드판매량
P4150
P1120
P390
정답 및 해설

WITH TIES 는 세 번째 행의 판매량 90 과 같은 값을 가진 행을 모두 함께 가져오므로 P4·P1 에 90 인 P3·P5·P6 이 더해져 5행이 된다.

NULLS LAST 로 판매량 NULL 인 P2 는 맨 뒤로 밀려 제외되고, 바깥 ORDER BY 가 동점 행을 상품코드 순으로 정렬한다.

  • 3행만 나온 표는 ROWS ONLY 처럼 동점 행을 버린 경우다.
  • P2 가 들어간 표는 NULLS LAST 를 무시하고 Oracle 기본 정렬을 적용한 경우다.
  • P7 이 들어간 표는 판매량을 오름차순으로 정렬해 뽑은 경우다.
문제 41
Oracle(12c 이상)에서 매출 상위 3개 지점을 구하려 한다. 매출에 NULL 과 동점이 없을 때, 의도한 결과를 보장하지 않는 SQL은?
㉠ SELECT 지점명, 매출
     FROM (SELECT 지점명, 매출 FROM 지점 ORDER BY 매출 DESC)
    WHERE ROWNUM <= 3;

㉡ SELECT 지점명, 매출
     FROM 지점
    WHERE ROWNUM <= 3
    ORDER BY 매출 DESC;

㉢ SELECT 지점명, 매출
     FROM 지점
    ORDER BY 매출 DESC
    FETCH FIRST 3 ROWS ONLY;

㉣ SELECT 지점명, 매출
     FROM (SELECT 지점명, 매출,
                  ROW_NUMBER() OVER (ORDER BY 매출 DESC) AS RN
             FROM 지점)
    WHERE RN <= 3;
1㉠
2㉣
3㉢
4㉡
정답 및 해설

ROWNUM 은 WHERE 단계에서 행을 읽는 순서대로 붙고 ORDER BY 는 그 뒤에 실행되므로, ㉡은 먼저 걸러진 임의의 3행을 정렬할 뿐이다.

상위 N 개를 정확히 얻으려면 정렬을 인라인 뷰 안에서 끝낸 뒤 바깥에서 ROWNUM 을 걸어야 한다.

  • ㉠은 인라인 뷰에서 먼저 정렬한 뒤 ROWNUM 으로 자르므로 올바르다.
  • ㉢의 FETCH FIRST 는 ORDER BY 이후에 적용되는 12c 이상 표준 구문이다.
  • ㉣은 ROW_NUMBER 로 순번을 매긴 뒤 바깥에서 3 이하만 고르므로 올바르다.
문제 42
Oracle 기준으로 다음 SQL 결과의 다섯 번째 행에 나오는 이름은?

[직원]

사번이름상사사번
1한지민(null)
2윤서준1
3강하린1
4서도윤2
5김태오3
6남유진2
7노을5
SELECT LEVEL, 이름
  FROM 직원
 START WITH 상사사번 IS NULL
 CONNECT BY PRIOR 사번 = 상사사번
 ORDER SIBLINGS BY 이름;
1남유진
2서도윤
3강하린
4윤서준
정답 및 해설

계층은 깊이 우선으로 펼쳐지고 ORDER SIBLINGS BY 가 같은 상사를 둔 형제끼리만 이름순으로 정렬하므로, 순서는 한지민·강하린·김태오·노을·윤서준·남유진·서도윤이다.

강하린이 윤서준보다 앞서므로 강하린의 하위 전체(김태오·노을)가 먼저 나온 뒤 윤서준 가지로 넘어간다.

  • 서도윤은 계층을 무시하고 전체를 이름순으로 정렬한 ORDER BY 의 결과이다.
  • 남유진은 레벨별로 한 층씩 나열한다고 본 결과이다.
  • 강하린은 형제를 사번순으로 펼친다고 본 결과이다.
문제 43
다음 SQL 에 대한 설명으로 옳은 것은?

[직원]

사번이름관리자사번
1정은(null)
2하람1
3류진1
4백설2
SELECT A.이름 AS 직원, B.이름 AS 관리자
  FROM 직원 A
  JOIN 직원 B
    ON A.관리자사번 = B.사번;
1관리자가 없는 정은도 결과에 나온다
2관리자 열에는 상사의 이름이 나온다
3결과 행 수는 직원 수와 항상 같다
4같은 테이블은 별칭 없이 두 번 쓴다
정답 및 해설

셀프 조인에서 A 는 직원, B 는 관리자 역할을 하며, A 의 관리자사번과 B 의 사번이 같은 행을 연결하므로 관리자 열에는 상사의 이름이 나온다.

결과는 하람-정은, 류진-정은, 백설-하람의 3행이다.

  • INNER 조인이라 관리자사번이 NULL 인 정은은 결과에서 빠진다.
  • 같은 테이블을 두 번 쓰려면 반드시 서로 다른 별칭으로 구분해야 한다.
  • 정은이 빠져 3행이므로 직원 수 4와 다르다.
문제 44
다음 SQL 결과에서 NULL 로 표시되는 셀은 모두 몇 개인가?

[배송]

배송번호지역상태
1서울완료
2서울지연
3서울완료
4부산완료
5부산반송
6대구지연
7대구지연
8(null)완료
SELECT 지역,
       SUM(CASE WHEN 상태 = '완료' THEN 1 ELSE 0 END) AS 완료,
       COUNT(CASE WHEN 상태 = '반송' THEN 1 END) AS 반송,
       SUM(CASE WHEN 상태 = '지연' THEN 1 END) AS 지연
  FROM 배송
 GROUP BY 지역
 ORDER BY 지역;
13
25
31
42
정답 및 해설

지연 열은 ELSE 가 없어 지연 건이 없는 부산과 지역 NULL 그룹에서 SUM 이 NULL 이 되고, 지역 NULL 그룹의 지역 셀까지 더해 NULL 셀은 3개이다.

반송 열은 COUNT 라 해당 건이 없으면 NULL 이 아닌 0이 되고, 완료 열은 ELSE 0 덕분에 모두 숫자이다.

  • 1개는 지역이 NULL 인 행도 하나의 그룹이 된다는 점을 놓친 경우이다.
  • 2개는 NULL 그룹의 지역 셀이나 지연 셀 중 하나만 센 경우이다.
  • 5개는 COUNT 도 대상이 없으면 NULL 을 준다고 보고 지역 셀을 빠뜨린 경우이다.
문제 45
Oracle 기준(대소문자 구분)으로 다음 SQL 의 결과 값은?

[회원]

회원ID
kim01
Lee22
park7
choi123
jung_05
han99
yoo 10
ab12
SELECT COUNT(*)
  FROM 회원
 WHERE REGEXP_LIKE(회원ID, '^[[:lower:]]+[[:digit:]]{2}$');
15
24
33
42
정답 및 해설

소문자 1자 이상 뒤에 숫자가 정확히 2자로 끝나야 하므로 kim01·han99·ab12 의 3건만 일치한다.

[[:lower:]] 는 소문자, [[:digit:]] 는 숫자를 뜻하고, ^ 와 $ 가 문자열 전체를 감싸 중간 일치를 허용하지 않는다.

  • Lee22 는 첫 글자가 대문자라 [[:lower:]] 와 맞지 않는다.
  • park7 은 숫자가 1자, choi123 은 3자라 {2} 와 $ 조건에 걸린다.
  • jung_05 와 yoo 10 은 밑줄과 공백이 문자 클래스에 없어 제외된다.
문제 46
다음 MERGE 문에 대한 설명으로 옳은 것은?
MERGE INTO 회원 T
USING 신규회원 S
   ON (T.회원번호 = S.회원번호)
 WHEN MATCHED THEN
   UPDATE SET T.등급 = S.등급
 WHEN NOT MATCHED THEN
   INSERT (회원번호, 이름, 등급)
   VALUES (S.회원번호, S.이름, S.등급);
1일치하지 않는 원본 행은 새로 삽입된다
2MERGE 는 DDL 이므로 자동 커밋된다
3원본에 없는 대상 행은 삭제된다
4일치하는 대상 행은 삭제 후 재입력된다
정답 및 해설

MERGE 는 ON 조건으로 원본(신규회원)과 대상(회원)을 비교해, 짝이 없는 원본 행은 WHEN NOT MATCHED 절에 따라 회원에 새로 삽입한다.

짝이 있는 행은 WHEN MATCHED 절에 따라 등급만 원본 값으로 갱신된다.

  • 일치하는 행은 삭제 없이 UPDATE 로 값만 바뀐다.
  • 원본에 없는 대상 행은 이 문장에서 아무 영향을 받지 않는다.
  • MERGE 는 DML 이라 COMMIT 이나 ROLLBACK 으로 확정·취소해야 한다.
문제 47
격리 수준이 READ UNCOMMITTED 일 때 다음 상황에서 세션 B 가 겪은 현상은?
[세션 A] UPDATE 지점 SET 매출 = 900 WHERE 지점코드 = 'A';
         -- 아직 COMMIT 하지 않음
[세션 B] SELECT 매출 FROM 지점 WHERE 지점코드 = 'A';
         -- 900 이 조회됨
[세션 A] ROLLBACK;
1Dirty Read
2Phantom Read
3Non-Repeatable Read
4Lost Update
정답 및 해설

세션 B 는 커밋되지 않았다가 결국 롤백된 900 을 읽었으므로 Dirty Read 이다.

READ COMMITTED 이상에서는 커밋된 데이터만 읽어 이 현상을 막으며, Oracle 은 변경 전 값을 보여 주는 읽기 일관성으로 처리한다.

  • Non-Repeatable Read 는 같은 행을 두 번 읽는 사이 커밋된 변경으로 값이 달라지는 현상이다.
  • Phantom Read 는 같은 조건 재조회 시 새 행이 나타나는 현상이다.
  • Lost Update 는 두 트랜잭션의 갱신이 겹쳐 한쪽 변경이 사라지는 현상이다.
문제 48
다음과 같이 정의된 상태에서 회원 테이블의 한 회원을 삭제하면, 그 회원이 쓴 게시글은 어떻게 되는가?
CREATE TABLE 게시글 (
  글번호     INT PRIMARY KEY,
  작성자번호 INT,
  CONSTRAINT FK_작성자 FOREIGN KEY (작성자번호)
    REFERENCES 회원 (회원번호)
    ON DELETE SET NULL
);
1작성자번호가 기본값으로 바뀐다
2해당 게시글도 함께 삭제된다
3작성자번호가 NULL 로 바뀐다
4자식 행 때문에 삭제가 거부된다
정답 및 해설

외래키에 ON DELETE SET NULL 을 지정했으므로 부모 회원이 삭제되면 해당 게시글의 작성자번호가 NULL 로 바뀌고 게시글 행은 남는다.

작성자번호 열에 NOT NULL 제약이 없어야 이 동작이 가능하며, 옵션을 생략하면 참조되는 부모 행 삭제가 거부된다.

  • 게시글까지 지우는 것은 ON DELETE CASCADE 의 동작이다.
  • 삭제 거부는 옵션을 생략하거나 RESTRICT 계열을 지정한 경우이다.
  • 기본값으로 바꾸는 것은 SET DEFAULT 이며 이 정의와 다르다.
문제 49
Oracle 에서 큰따옴표 없이 실행할 때 오류가 발생하는 CREATE TABLE 문은?
1CREATE TABLE 2ND_ZONE (ZONE_CD CHAR(3))
2CREATE TABLE BR$HIST (OPEN_DT DATE)
3CREATE TABLE DLV_LOG (MEMO VARCHAR2(200))
4CREATE TABLE MEM_PT (PT NUMBER(7,2))
정답 및 해설

테이블명은 문자로 시작해야 하므로 숫자로 시작하는 2ND_ZONE 은 큰따옴표 없이 쓰면 오류가 발생한다.

이름에는 문자·숫자와 _ $ # 만 쓸 수 있고, 예약어나 같은 스키마 안 중복 이름도 허용되지 않는다.

  • DLV_LOG 는 밑줄을 포함한 올바른 이름이고 VARCHAR2(200) 도 유효하다.
  • BR$HIST 처럼 $ 는 이름 중간에 쓸 수 있고 DATE 타입도 정상이다.
  • NUMBER(7,2) 는 전체 7자리 중 소수 2자리를 뜻하는 유효한 타입이다.
문제 50
Oracle 에서 오브젝트 권한(Object Privilege)을 부여하는 문장은?
1GRANT CREATE ANY VIEW TO CHOI
2GRANT CREATE TABLE TO KIM
3GRANT CREATE SESSION TO PARK
4GRANT UPDATE ON 직원 TO LEE
정답 및 해설

오브젝트 권한은 특정 객체에 대한 SELECT·INSERT·UPDATE·DELETE 등의 권한으로, ON 직원 처럼 대상 객체를 지정해 부여한다.

CREATE TABLE·CREATE SESSION 처럼 데이터베이스 전체에서 특정 작업을 할 수 있게 하는 것은 시스템 권한이다.

  • CREATE TABLE 은 자기 스키마에 테이블을 만들 수 있는 시스템 권한이다.
  • CREATE SESSION 은 접속을 허용하는 시스템 권한이다.
  • CREATE ANY VIEW 는 다른 스키마에도 뷰를 만들 수 있는 시스템 권한이다.
SQLD SQL 개발자 실전 모의고사모두CBT · moducbt.com
SQL 개발자 기출유형 모의고사 3회
총 50문항 · 객관식 · 정답·해설 포함
문제 1
ANSI-SPARC 3단계 스키마 구조에 대한 설명으로 옳은 것은?
1내부 스키마는 사용자별 뷰를 정의한다
2개념 스키마는 물리 저장 구조를 정의한다
3외부 스키마는 사용자별 관점을 정의한다
4논리적 독립성은 내부 스키마와 관련된다
정답 및 해설

외부 스키마는 사용자나 응용 프로그램마다 필요한 데이터를 바라보는 관점을 정의한 것이다.

개념 스키마는 조직 전체의 통합 구조, 내부 스키마는 물리 저장 구조를 정의하며 이 층 분리로 데이터 독립성이 확보된다.

  • 물리 저장 구조(레코드 형식·인덱스)를 정의하는 것은 내부 스키마다.
  • 사용자별 뷰를 정의하는 것은 외부 스키마다.
  • 논리적 독립성은 개념 스키마가 바뀌어도 외부 스키마가 유지되는 성질이다.
문제 2
엔터티 이름을 정하는 원칙으로 적절하지 않은 것은?
1약어 사용은 가능한 한 피한다
2업무 영역이 다르면 같은 이름을 쓴다
3현업에서 쓰는 업무 용어로 이름을 짓는다
4가급적 단수 명사로 이름을 붙인다
정답 및 해설

엔터티 이름은 모델 전체에서 유일해야 하므로 업무 영역이 달라도 같은 이름을 다시 쓰지 않는다.

그 밖에 현업 업무 용어 사용, 약어 지양, 단수 명사, 생성 의미대로 이름 붙이기가 명명 원칙에 해당한다.

  • 현업 업무 용어 사용은 명명 원칙에 맞는다.
  • 약어를 피하는 것은 명명 원칙에 맞는다.
  • 단수 명사 사용은 명명 원칙에 맞는다.
문제 3
속성과 도메인(Domain)에 대한 설명으로 가장 적절한 것은?
1속성명에 약어를 붙여 의미를 구분한다
2한 속성에 여러 값을 묶어 저장한다
3한 속성은 여러 도메인을 가질 수 있다
4도메인은 속성의 허용 값 범위를 정한다
정답 및 해설

도메인은 각 속성이 가질 수 있는 값의 범위(데이터 타입·길이·허용 값)를 정한 것이다.

속성 하나는 도메인 하나에 속하고, 한 인스턴스의 속성에는 하나의 값만 저장하는 것이 원칙이다.

  • 속성은 하나의 도메인만 가진다.
  • 속성명은 약어를 피하고 업무에서 쓰는 용어로 짓는다.
  • 여러 값을 한 속성에 묶으면 1정규형을 위반한다.
문제 4
다음 ERD에 대한 설명으로 적절하지 않은 것은?

그림

1부서와 직원은 존재에 의한 관계다
2직원 간 관리 관계는 자기참조 관계다
3직원과 매출은 행위에 의한 관계다
4자기참조는 다른 두 엔터티 간 관계다
정답 및 해설

자기참조(재귀) 관계는 같은 엔터티의 인스턴스끼리 맺는 관계로, 직원의 관리자번호가 같은 직원 엔터티의 직원번호를 참조하는 경우다.

소속처럼 상태로 연결되면 존재에 의한 관계, 등록처럼 업무 행위로 연결되면 행위에 의한 관계다.

  • 직원이 부서에 소속되는 것은 존재에 의한 관계다.
  • 직원이 매출을 등록하는 것은 행위에 의한 관계다.
  • 관리자번호로 직원끼리 이어지는 것은 자기참조 관계다.
문제 5
두 엔터티의 관계를 비식별자 관계(점선)로 설정하는 것이 적절한 경우는?
1부모 키를 자식 PK로 상속해야 할 때
2자식이 부모와 생명주기를 함께할 때
3자식 식별에 부모 키가 꼭 필요할 때
4부모 없이도 자식 인스턴스가 생길 때
정답 및 해설

부모 없이도 자식이 생성될 수 있거나 생명주기가 따로 움직이면 비식별자 관계로 두고 부모 키를 일반 속성(FK)으로만 받는다.

반대로 부모에 강하게 종속되어 부모 키가 자식 식별에 필요하면 식별자 관계(실선)로 PK에 포함한다.

  • 생명주기를 함께하는 강한 종속은 식별자 관계 기준이다.
  • 자식 식별에 부모 키가 필요하면 식별자 관계다.
  • 부모 키를 PK로 상속하는 것이 곧 식별자 관계다.
문제 6
매출 엔터티 한 곳에 직원명·지점명까지 담던 모델을 정규화하여 다음 ERD로 분리하였다. 이에 대한 설명으로 가장 적절한 것은?

그림

1정규화로 조회 성능은 항상 좋아진다
2새 지점은 직원이 있어야 등록할 수 있다
3지점명을 보려면 매출에서 두 번 조인한다
4지점명 변경 시 매출 행도 함께 고친다
정답 및 해설

매출에는 직원번호만 있으므로 지점명을 얻으려면 매출→직원→지점으로 조인이 두 번 필요하다.

정규화는 수정·입력 이상을 없애지만 흩어진 데이터를 다시 모으는 조인이 늘어 조회 성능은 떨어질 수 있다.

  • 지점명은 지점 엔터티 한 행만 고치면 되므로 매출 수정이 필요 없다.
  • 조인이 늘어 조회가 느려질 수 있으므로 항상 좋아지지 않는다.
  • 지점이 독립 엔터티라 직원 없이도 등록할 수 있다.
문제 7
다음 ERD에서 각 게시글의 제목과 그 원글(상위 게시글)의 제목을 한 행으로 조회하는 방법으로 가장 적절한 것은?

그림

1게시글에 별칭 둘을 주어 셀프 조인한다
2원글 제목을 회원 엔터티로 옮긴다
3상위글번호에 UNIQUE 제약을 추가한다
4게시글과 회원을 카티션 곱으로 잇는다
정답 및 해설

상위글번호가 같은 게시글 엔터티의 글번호를 참조하는 계층(자기참조) 관계이므로 한 테이블을 두 번 읽는 셀프 조인으로 조회한다.

예컨대 자식 A 와 부모 B 두 별칭을 두고 A.상위글번호 = B.글번호 로 연결하며, 원글이 없는 글까지 보려면 OUTER JOIN 을 쓴다.

  • 카티션 곱은 모든 조합을 만들 뿐 원글을 찾지 못한다.
  • 원글 하나에 답글이 여럿이므로 UNIQUE 제약은 맞지 않는다.
  • 원글 제목은 게시글 속성이라 회원으로 옮길 이유가 없다.
문제 8
업무 규칙상 배송 1건은 배송품목 1건 이상과 함께 하나의 트랜잭션으로 등록되어야 한다. 다음 데이터와 모델에 대한 설명으로 옳은 것은?

[배송]

배송번호회원번호등록일
B01M72026-04-01
B02M32026-04-01
B03M72026-04-02

[배송품목]

배송번호품목순번품목명수량
B011머그컵2
B012텀블러1
B021우산1
1배송품목은 배송과 따로 커밋해도 된다
2B03은 품목 없이 등록돼 규칙을 어겼다
3배송 쪽의 품목 참여는 선택으로 그린다
4B01은 품목이 두 건이라 규칙을 어겼다
정답 및 해설

B03 은 배송품목이 한 건도 없으므로 배송과 품목을 하나의 트랜잭션으로 함께 등록하라는 규칙을 어긴 데이터다.

이런 업무 규칙은 모델에서 배송 쪽의 배송품목 관계를 필수 참여로 표현하고, 두 INSERT 를 한 단위로 커밋해 보장한다.

  • 품목 1건 이상이면 되므로 두 건인 B01 은 규칙에 맞다.
  • 따로 커밋하면 품목 없는 배송이 생길 수 있어 규칙을 깬다.
  • 반드시 함께 생기는 관계는 선택이 아니라 필수로 그린다.
문제 9
NULL에 대한 설명으로 적절하지 않은 것은?
1Oracle은 ''를 NULL로 처리해 저장한다
2NULL과의 산술 연산은 NULL이다
3SQL Server는 ''를 NULL로 본다
4NULL은 0이나 공백과 다른 값이다
정답 및 해설

SQL Server는 빈 문자열('')을 길이 0인 문자열로 저장하므로 NULL 과 구분하며, 빈 문자열을 NULL 로 바꾸는 것은 Oracle의 동작이다.

NULL 은 값이 없거나 알 수 없음을 뜻하므로 숫자 0, 공백 문자와 다르다.

  • NULL 은 0·공백과 다른 미지의 값이다.
  • NULL 이 섞인 사칙 연산 결과는 NULL 이다.
  • Oracle 은 빈 문자열을 입력하면 NULL 로 저장한다.
문제 10
인조식별자에 대한 설명으로 적절하지 않은 것은?
1시퀀스 등으로 순번을 자동 부여한다
2같은 데이터의 중복 입력을 막아 준다
3복잡한 복합 식별자를 단순하게 바꾼다
4불필요한 인덱스가 늘어날 수 있다
정답 및 해설

인조식별자는 값이 매번 새로 매겨지므로 같은 업무 데이터가 두 번 들어와도 막지 못해 중복 데이터가 생길 수 있다.

그래서 본질식별자 열에 유니크 인덱스를 따로 두는 경우가 많고, 그만큼 인덱스가 늘어나는 단점이 있다.

  • 시퀀스·IDENTITY 로 자동 부여하는 것이 인조식별자의 특징이다.
  • 여러 속성으로 된 복합 식별자를 단일 열로 단순화한다.
  • 본질식별자 중복 방지용 인덱스가 추가로 필요해진다.
문제 11
다음 상황이 보여 주는 트랜잭션의 특성은?

배송 상태를 '완료'로 바꾸고 COMMIT 한 직후 DB 서버가 비정상 종료되었다. 재기동한 뒤 조회해도 해당 배송은 '완료' 상태로 남아 있었다.

1일관성(Consistency)
2고립성(Isolation)
3원자성(Atomicity)
4지속성(Durability)
정답 및 해설

커밋이 완료된 변경은 장애가 나도 사라지지 않고 영구히 반영되어야 한다는 특성이 지속성(Durability)이다.

DBMS 는 커밋 시점의 로그를 디스크에 남겨 재기동할 때 커밋된 변경을 복구한다.

  • 원자성은 트랜잭션 연산이 전부 반영되거나 전부 취소되는 성질이다.
  • 일관성은 실행 전후 DB 가 무결성 제약을 만족하는 성질이다.
  • 고립성은 동시에 실행되는 트랜잭션끼리 간섭하지 않는 성질이다.
문제 12
다음 SQL의 실행 결과로 옳은 것은?

[직원]

직원번호기본급수당
130050
2250(null)
328030
4(null)40
53200
SELECT SUM(기본급 + 수당) AS S1,
       SUM(기본급) + SUM(수당) AS S2,
       COUNT(기본급 + 수당) AS CNT
  FROM 직원;
1
S1S2CNT
9809803
2
S1S2CNT
127012705
3
S1S2CNT
98012703
4
S1S2CNT
98012705
정답 및 해설

기본급 + 수당은 행마다 먼저 계산되어 어느 한쪽이 NULL 인 직원번호 2·4 는 NULL이 되고, 집계 함수는 이 NULL 을 빼고 계산해 S1=350+310+320=980, CNT=3 이다.

반면 S2 는 열별로 NULL 을 제외해 먼저 합한 뒤 더하므로 1150+120=1270이 된다.

  • NULL 을 0 으로 보면 S1 이 1270, CNT 가 5 가 되지만 실제와 다르다.
  • COUNT(식)은 식이 NULL 인 행을 세지 않아 5 가 아니다.
  • S2 는 열마다 따로 합하므로 S1 과 같은 980 이 아니다.
문제 13
SELECT 문에 대한 설명으로 옳은 것은?
1열 별칭은 WHERE 절에서 쓸 수 없다
2DISTINCT는 첫 번째 열에만 적용된다
3덧셈이 곱셈보다 먼저 계산된다
4Oracle 연결 연산자는 + 기호다
정답 및 해설

WHERE 절은 SELECT 절보다 먼저 처리되므로 SELECT 절에서 붙인 열 별칭을 WHERE 절에서 참조할 수 없고, ORDER BY 절에서는 쓸 수 있다.

논리 실행 순서는 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 이다.

  • DISTINCT 는 SELECT 목록의 모든 열 조합에 적용된다.
  • 곱셈·나눗셈이 덧셈·뺄셈보다 먼저 계산된다.
  • Oracle 의 연결 연산자는 || 이고 + 는 SQL Server 방식이다.
문제 14
Oracle 기준으로, 출고일(DATE, 시각 포함)이 2026년 3월인 배송만 다음 결과처럼 조회하려 한다. ( ㉠ )에 들어갈 조건으로 옳은 것은? (세션 기본 날짜 형식은 RR/MM/DD)

[배송]

배송번호출고일기사명
D012026-02-28 23:40정우
D022026-03-01 09:00서진
D032026-03-15 13:20정우
D042026-03-31 18:05하린
D052026-04-01 00:00서진
D062025-03-20 10:30하린
SELECT 배송번호, TO_CHAR(출고일, 'YYYY-MM-DD HH24:MI') AS 출고
  FROM 배송
 WHERE ( ㉠ )
 ORDER BY 배송번호;

[원하는 결과]

배송번호출고
D022026-03-01 09:00
D032026-03-15 13:20
D042026-03-31 18:05
1TO_CHAR(출고일, 'YYYYMM') = '202603'
2EXTRACT(MONTH FROM 출고일) = 3
3SUBSTR(출고일, 1, 6) = '202603'
4TRUNC(출고일) = TO_DATE('202603', 'YYYYMM')
정답 및 해설

TO_CHAR(출고일, 'YYYYMM') 은 시각과 무관하게 연월 문자열만 만들어 비교하므로 3월 1일 09시부터 31일 18시까지의 D02·D03·D04 만 남는다.

DATE 를 문자열로 비교할 때는 형식 모델을 명시해야 하며, 형식 없이 문자 함수에 넣으면 세션 기본 형식으로 암시적 형변환된다.

  • TO_DATE('202603','YYYYMM') 은 3월 1일 0시라 TRUNC 결과가 같은 D02 만 나온다.
  • 월만 비교하면 2025년 3월의 D06 까지 포함된다.
  • 기본 형식으로 바뀐 '26/03/15' 앞 6자는 '202603' 과 같아질 수 없다.
문제 15
Oracle 기준으로 다음 SQL을 실행한 결과로 옳은 것은?

[회원]

회원번호등급가입일
M01G2026-01-05
M02S2026-01-09
M03(null)2026-02-11
M04G2026-02-20
M05B2026-03-02
SELECT 회원번호,
       DECODE(등급, NULL, 'N', 'G', 'Y', 'X') AS D,
       CASE 등급 WHEN NULL THEN 'N'
                 WHEN 'G'  THEN 'Y'
                 ELSE 'X' END AS C
  FROM 회원
 ORDER BY 회원번호;
1
회원번호DC
M01YY
M02XX
M03NX
M04YY
M05XX
2
회원번호DC
M01YY
M02XX
M03XX
M04YY
M05XX
3
회원번호DC
M01YY
M02XX
M03XN
M04YY
M05XX
4
회원번호DC
M01YY
M02XX
M03NN
M04YY
M05XX
정답 및 해설

DECODE 는 비교 대상과 검색값이 모두 NULL 이면 같다고 보므로 M03 의 D 는 'N' 이 되지만, 단순 CASE 의 WHEN NULL 은 등급 = NULL 비교라 결과가 UNKNOWN 이어서 ELSE 의 'X' 가 나온다.

나머지 회원은 G 면 'Y', S·B 면 'X' 로 두 열이 같다.

  • CASE 도 NULL 을 잡는다고 본 결과로 M03 의 C 가 틀렸다.
  • DECODE 도 NULL 을 못 잡는다고 본 결과로 M03 의 D 가 틀렸다.
  • 두 함수의 NULL 처리를 서로 뒤바꾼 결과다.
문제 16
Oracle 기준으로 다음 SQL을 실행한 결과로 옳은 것은?

[매출]

매출번호지점코드금액
S1B101250
S2B101349
S3B20-1250
S4B2080
SELECT 매출번호,
       ROUND(금액, -2) AS R,
       TRUNC(금액, -2) AS T,
       MOD(금액, 300)  AS M
  FROM 매출
 ORDER BY 매출번호;
1
매출번호RTM
S11300120050
S213001300149
S3-1300-1200-50
S40080
2
매출번호RTM
S11300120050
S213001300149
S3-1300-1200-50
S4100080
3
매출번호RTM
S11300120050
S213001300149
S3-1300-1200250
S4100080
4
매출번호RTM
S11300120050
S213001300149
S3-1200-1200-50
S4100080
정답 및 해설

자릿수 -2 는 십의 자리에서 처리하므로 ROUND 는 -1250 을 절댓값 기준으로 올려 -1300, 80 을 100 으로 만들고, TRUNC 는 버리기만 해 -1200·0 이 된다.

MOD 는 나머지의 부호가 피제수를 따르므로 -1250 을 300 으로 나눈 나머지는 -50 이다.

  • -1250 의 반올림을 큰 쪽으로 올린 -1200 은 ROUND 결과가 아니다.
  • 나머지를 양수로 맞춘 250 은 Oracle MOD 결과가 아니다.
  • 80 은 십의 자리 8 이 5 이상이라 0 이 아니라 100 으로 반올림된다.
문제 17
다음 SQL의 결과로 출력되는 글번호를 모두 나열한 것은?

[게시글]

글번호분류조회수
101공지50
102질문120
103(null)30
104질문(null)
105자유80
106(null)150
107자유100
SELECT 글번호
  FROM 게시글
 WHERE NOT (분류 = '공지' OR 조회수 > 100)
 ORDER BY 글번호;
1103, 105, 107
2105, 107
3105
4104, 105, 107
정답 및 해설

괄호 안이 FALSE 인 105·107 만 NOT 을 거쳐 TRUE 가 되며, 103(분류 NULL)과 104(조회수 NULL)는 괄호 안이 UNKNOWN 이고 NOT UNKNOWN 도 UNKNOWN 이라 제외된다.

106 은 분류가 NULL 이어도 조회수 150 > 100 이 TRUE 라 OR 전체가 TRUE 가 되어 빠진다.

  • 107 의 조회수 100 은 100 초과가 아니므로 결과에 포함된다.
  • 분류가 NULL 인 103 을 FALSE 로 보면 잘못 포함된다.
  • 조회수가 NULL 인 104 도 UNKNOWN 이라 출력되지 않는다.
문제 18
[기준 조건]과 항상 같은 결과를 내는 후보 조건은?

[기준 조건]

WHERE NOT 지점 = 'A' AND 금액 >= 100 OR 직급 = '과장'

[후보 조건]

㉠ WHERE NOT (지점 = 'A' AND 금액 >= 100) OR 직급 = '과장'
㉡ WHERE (지점 <> 'A' AND 금액 >= 100) OR 직급 = '과장'
㉢ WHERE 지점 <> 'A' AND (금액 >= 100 OR 직급 = '과장')
㉣ WHERE NOT (지점 = 'A' AND 금액 >= 100 OR 직급 = '과장')
1㉡
2㉢
3㉠
4㉣
정답 및 해설

논리 연산자는 NOT → AND → OR 순으로 결합하므로 기준 조건은 ((NOT 지점 = 'A') AND 금액 >= 100) OR 직급 = '과장' 과 같고, NOT 지점 = 'A' 는 지점 <> 'A' 와 같다.

따라서 ㉡처럼 AND 묶음을 먼저 만든 뒤 OR 로 직급 조건을 더한 형태가 동일하다.

  • ㉠은 NOT 이 AND 묶음 전체에 걸려 의미가 달라진다.
  • ㉢은 OR 를 AND 보다 먼저 묶어 과장도 지점 A 면 빠진다.
  • ㉣은 NOT 이 조건 전체를 부정해 정반대에 가깝다.
문제 19
다음 SQL 중 실행 시 오류가 발생하는 것은?

[매출]

매출번호지점코드금액
1A300
2A250
3B90
4B400
5C120
㉠ SELECT 지점코드, SUM(금액)
     FROM 매출
    GROUP BY 지점코드
   HAVING COUNT(*) >= 2;

㉡ SELECT SUM(금액)
     FROM 매출
   HAVING SUM(금액) > 0;

㉢ SELECT 지점코드, SUM(금액)
     FROM 매출
    WHERE SUM(금액) > 500
    GROUP BY 지점코드;

㉣ SELECT 지점코드, COUNT(*)
     FROM 매출
    WHERE 금액 > 100
    GROUP BY 지점코드
   HAVING 지점코드 <> 'B';
1㉢
2㉡
3㉠
4㉣
정답 및 해설

WHERE 절은 그룹이 만들어지기 전에 행 단위로 처리되므로 집계 함수를 쓸 수 없고, 그룹 조건 SUM(금액) > 500 은 HAVING 절로 옮겨야 한다.

HAVING 에는 SELECT 목록에 없는 집계나 GROUP BY 열도 조건으로 쓸 수 있다.

  • ㉠은 SELECT 에 없는 COUNT(*) 를 HAVING 에 써도 정상 실행된다.
  • ㉡은 GROUP BY 없이 테이블 전체를 한 그룹으로 보고 HAVING 을 적용한다.
  • ㉣은 WHERE 로 행을 거른 뒤 GROUP BY 열 조건을 HAVING 에 둔 형태라 정상이다.
문제 20
다음 SQL의 결과 행 수는?

[배송]

배송번호지역상태
D1서울완료
D2서울완료
D3서울취소
D4부산완료
D5부산(null)
D6(null)완료
D7(null)대기
D8부산대기
D9서울대기
D10부산(null)
SELECT 지역, 상태, COUNT(*) AS 건수
  FROM 배송
 WHERE 상태 <> '취소'
 GROUP BY 지역, 상태
 ORDER BY 지역, 상태;
15
27
36
44
정답 및 해설

상태 <> '취소' 는 D3 뿐 아니라 상태가 NULL 인 D5·D10 도 UNKNOWN 이라 제외하고, 남은 7행을 (지역, 상태)로 묶으면 NULL 지역도 하나의 그룹이 된다.

그룹은 (서울,완료)·(서울,대기)·(부산,완료)·(부산,대기)·(NULL,완료)·(NULL,대기) 의 6개다.

  • 지역이 NULL 인 두 그룹을 빼면 4행이 되지만 GROUP BY 는 NULL 을 버리지 않는다.
  • NULL 지역을 한 그룹으로 합치면 5행이지만 상태가 달라 두 그룹이다.
  • 상태 NULL 행을 남겨 (부산,NULL) 그룹을 만들면 7행으로 잘못 센다.
문제 21
다음 SQL의 실행 결과로 옳은 것은?

[게시글]

글번호작성자추천수
1민호10
2수빈(null)
3민호20
4지안0
5수빈(null)
6지안30
SELECT COUNT(*)      AS A,
       COUNT(추천수) AS B,
       AVG(추천수)   AS C
  FROM 게시글;
1
ABC
6415
2
ABC
6610
3
ABC
4415
4
ABC
6410
정답 및 해설

COUNT(*) 는 NULL 여부와 관계없이 6행을 모두 세고, COUNT(추천수) 는 NULL 2건을 빼고 0 은 값이므로 포함해 4 이다.

AVG 도 NULL 을 뺀 4건으로 나누어 (10+20+0+30)/4 = 15 가 된다.

  • NULL 을 0 으로 보면 B=6, C=10 이 되지만 집계 함수는 NULL 을 제외한다.
  • COUNT(*) 는 NULL 이 있는 행도 세므로 4 가 아니다.
  • AVG 는 전체 행 수 6 이 아니라 NULL 아닌 4건으로 나눈다.
문제 22
다음 SQL의 실행 결과로 옳은 것은? (부서명은 가나다순으로 비교된다)

[직원]

직원번호부서급여
E1영업300
E2개발400
E3영업250
E4인사300
E5개발350
E6영업300
SELECT 직원번호, 부서 AS 소속, 급여
  FROM 직원
 ORDER BY 소속 DESC, 3, 1 DESC;
1
직원번호소속급여
E4인사300
E6영업300
E1영업300
E3영업250
E2개발400
E5개발350
2
직원번호소속급여
E5개발350
E2개발400
E3영업250
E6영업300
E1영업300
E4인사300
3
직원번호소속급여
E4인사300
E3영업250
E1영업300
E6영업300
E5개발350
E2개발400
4
직원번호소속급여
E4인사300
E3영업250
E6영업300
E1영업300
E5개발350
E2개발400
정답 및 해설

소속 내림차순이라 인사→영업→개발 순이고, 같은 부서 안에서는 열 번호 3(급여) 오름차순, 급여가 같으면 열 번호 1(직원번호) 내림차순으로 E6 이 E1 앞에 온다.

DESC 는 바로 앞의 정렬 키 하나에만 적용되고 방향을 생략한 키는 ASC 다.

  • E1 을 E6 앞에 둔 표는 마지막 키의 DESC 를 무시했다.
  • 급여까지 내림차순으로 정렬한 표는 DESC 가 모든 키에 걸린다고 오해했다.
  • 개발부터 시작한 표는 소속을 오름차순으로 정렬했다.
문제 23
다음 두 SQL ㉮, ㉯의 결과 행 수를 순서대로 나타낸 것은?

[지점]

지점코드지점명
B1강남
B2판교
B3수원

[직원]

직원번호지점코드
E1B1
E2B1
E3B2
E4(null)

[매출]

매출번호직원번호금액
S1E1100
S2E1200
S3E3150
S4E4300
S5E950
㉮ SELECT B.지점명, S.매출번호, S.금액
     FROM 지점 B, 직원 E, 매출 S
    WHERE E.직원번호 = S.직원번호;

㉯ SELECT B.지점명, S.매출번호, S.금액
     FROM 지점 B, 직원 E, 매출 S
    WHERE E.직원번호 = S.직원번호
      AND B.지점코드 = E.지점코드;
112, 3
212, 4
34, 3
415, 3
정답 및 해설

㉮는 직원·매출만 연결하고 지점과의 조건이 없어, 직원번호가 맞는 매출 4건(S1·S2·S3·S4)에 지점 3행이 카티션 곱으로 붙어 12행이다.

㉯처럼 테이블 3개에는 N-1=2개 조인 조건이 필요하며, 지점코드가 NULL 인 E4 의 S4 는 조인에서 빠져 3행이 된다.

  • 12와 4 는 지점코드 NULL 인 직원도 조인된다고 본 결과다.
  • 15 는 직원이 없는 S5 까지 곱한 수로, 직원과의 조건에서 이미 빠진다.
  • 4 는 빠진 조인 조건이 행을 늘리지 않는다고 본 결과다.
문제 24
다음 SQL의 실행 결과에 대한 설명으로 옳은 것은?

[직원]

직원번호직원명관리자번호
101윤서(null)
102도윤101
103하은101
104시우102
105지아(null)
SELECT A.직원명 AS 직원, B.직원명 AS 관리자
  FROM 직원 A
  JOIN 직원 B
    ON A.관리자번호 = B.직원번호
 ORDER BY A.직원번호;
1관리자가 없는 윤서도 직원 열에 나온다
2같은 테이블이라 별칭 없이 실행된다
3시우의 관리자로 윤서가 출력된다
4관리자 열에 윤서가 두 번 출력된다
정답 및 해설

A 를 직원, B 를 관리자로 보는 셀프 조인에서 관리자번호가 있는 도윤·하은·시우만 연결되어 3행이 나오며, 도윤·하은의 관리자가 모두 윤서다.

INNER JOIN 이므로 관리자번호가 NULL 인 윤서·지아는 직원 열에 나오지 않는다.

  • 윤서까지 보려면 LEFT OUTER JOIN 이 필요하다.
  • 한 번의 조인은 바로 위 한 단계만 이어 시우의 관리자는 도윤이다.
  • 같은 테이블을 두 번 쓰면 별칭이 없을 때 열을 구분할 수 없다.
문제 25
다음 결과를 얻기 위해 ( ㉠ )에 들어갈 구문으로 옳은 것은?

[부서]

부서코드부서명등록일
D1영업2026-01-02
D2개발2026-01-02
D3인사2026-02-01

[직원]

직원번호직원명부서코드등록일
201서아D12026-01-02
202준호D22026-03-05
203예린D12026-02-01
204태민D32026-02-01
205유나D22026-01-02
SELECT 직원번호, 부서코드, 부서명
  FROM 직원 ( ㉠ )
 ORDER BY 직원번호;

[원하는 결과]

직원번호부서코드부서명
201D1영업
202D2개발
203D1영업
204D3인사
205D2개발
1LEFT JOIN 부서 USING (등록일)
2JOIN 부서 USING (부서코드)
3JOIN 부서 ON 직원.부서코드 = 부서.부서코드
4NATURAL JOIN 부서
정답 및 해설

두 테이블에는 부서코드와 등록일이 함께 있으므로 부서코드 하나로만 조인하려면 USING (부서코드) 를 써야 하고, USING 열은 접두사 없이 SELECT 할 수 있다.

NATURAL JOIN 은 이름이 같은 열을 모두 조건으로 써 등록일까지 같은 201·204·205 의 3행만 남는다.

  • NATURAL JOIN 은 등록일이 다른 202·203 을 빠뜨린다.
  • 등록일로 조인하면 부서가 엉뚱하게 짝지어지고 부서코드도 모호해진다.
  • ON 절을 쓰면 접두사 없는 부서코드가 모호하다는 오류가 난다.
문제 26
Oracle 기준으로 다음 ㉠의 결과 행 수와 ㉡의 결과 값을 순서대로 나열한 것은?

[부서]

부서코드부서명
10기획
20영업
30개발
40인사

[직원]

사번이름부서코드급여
101김윤10450
102이솔10280
103박건20320
104최담20350
105정우30250
106조현30260
107한결(null)500

㉠

SELECT D.부서명, E.이름
  FROM 부서 D, 직원 E
 WHERE D.부서코드 = E.부서코드(+)
   AND E.급여(+) >= 300;

㉡

SELECT COUNT(*) FROM 부서 CROSS JOIN 직원;
13, 28
27, 24
35, 28
45, 24
정답 및 해설

㉠은 E.급여(+) >= 300 까지 (+) 가 붙어 LEFT OUTER JOIN 의 ON 조건이 되므로 5행이고, ㉡의 CROSS JOIN 은 4×7=28행이다.

㉠에서 기획은 김윤, 영업은 박건·최담과 결합하고, 급여 300 미만뿐인 개발과 직원이 없는 인사는 직원 열이 NULL 인 한 행씩 남는다.

  • 3행은 급여 조건을 WHERE 필터로 본 결과로, 개발·인사 행이 사라진다.
  • 7행은 급여 조건을 무시하고 부서코드만으로 외부 조인한 결과이다.
  • 24는 부서코드가 NULL 인 직원을 곱에서 뺀 값이나, CROSS JOIN 은 조건 없이 모든 행을 곱한다.
문제 27
지점을 왼쪽, 매출을 오른쪽 테이블로 두고 지점코드로 조인할 때, 설명으로 적절하지 않은 것은?

[지점]

지점코드지점명
A강남
B판교
C수원

[매출]

매출번호지점코드금액
1A100
2A200
3B150
4D80
1INNER JOIN 결과는 3행이다
2LEFT 조인에서 수원은 NULL 과 결합된다
3FULL OUTER JOIN 결과는 5행이다
4RIGHT 조인에서 D 매출 행은 빠진다
정답 및 해설

RIGHT OUTER JOIN 은 오른쪽 매출 테이블의 모든 행을 남기므로, 짝이 없는 지점코드 D 매출도 지점 열이 NULL 인 채 결과에 포함된다.

매칭 행은 A 두 건과 B 한 건이며, 각 외부 조인은 기준 쪽의 짝 없는 행을 NULL 과 결합해 덧붙인다.

  • INNER JOIN 은 양쪽에 모두 있는 A 두 건과 B 한 건으로 3행이다.
  • LEFT 조인은 지점이 기준이므로 매출이 없는 수원이 NULL 과 결합된다.
  • FULL OUTER JOIN 은 매칭 3행에 수원과 D 매출이 더해져 5행이다.
문제 28
서브쿼리(Subquery)의 동작으로 적절하지 않은 것은?
1다중행 결과를 = 로 비교하면 오류다
2HAVING 절에도 서브쿼리를 쓸 수 있다
3결과가 0행이면 반드시 오류가 발생한다
4EXISTS 는 행의 존재 여부만 따진다
정답 및 해설

서브쿼리 결과가 0행이어도 오류가 나지 않으며, 비교 대상이 없으니 조건을 만족하는 행이 없을 뿐이고 스칼라 서브쿼리는 NULL 로 처리된다.

오류는 단일행 연산자로 비교하는 서브쿼리가 두 행 이상을 돌려줄 때 생긴다.

  • EXISTS 는 서브쿼리의 SELECT 값과 무관하게 행이 하나라도 있으면 참이다.
  • = 같은 단일행 연산자에 여러 행이 오면 오류가 나므로 IN 등을 써야 한다.
  • HAVING 절에 서브쿼리를 두어 그룹 집계 결과와 비교할 수 있다.
문제 29
다음 ㉠과 ㉡의 결과 값을 순서대로 나열한 것은?

[회원]

회원번호이름
1서하
2민재
3유나
4태윤
5지오

[게시글]

글번호작성자번호제목
111가입 인사
121질문
133후기
14(null)탈퇴회원 글
154공지 문의

㉠

SELECT COUNT(*) FROM 회원
 WHERE 회원번호 NOT IN (SELECT 작성자번호 FROM 게시글);

㉡

SELECT COUNT(*) FROM 회원 M
 WHERE NOT EXISTS (SELECT 1 FROM 게시글 P
                    WHERE P.작성자번호 = M.회원번호);
10, 2
22, 2
32, 0
41, 2
정답 및 해설

㉠은 서브쿼리 결과에 NULL 이 섞여 NOT IN 비교가 어떤 회원에서도 참이 되지 못해 0이고, ㉡의 NOT EXISTS 는 NULL 과 무관하게 글이 없는 민재·지오를 세어 2이다.

x NOT IN (1, 3, NULL, 4) 는 x <> NULL 비교가 UNKNOWN 이므로 결과가 참이 될 수 없다.

  • ㉠을 2로 본 것은 NULL 행을 무시한다고 착각한 경우이다.
  • ㉡을 0으로 본 것은 NOT EXISTS 도 NULL 에 영향받는다고 본 경우이다.
  • ㉠을 1로 볼 근거는 없으며, NULL 하나가 전체 비교를 막는다.
문제 30
다음 [결과]를 얻기 위해 ㉠에 들어갈 연산자로 옳은 것은?

[직원]

사번이름부서급여
1강민영업300
2고은영업450
3문수개발500
4배진개발200
5신혜인사600
6오현인사300
SELECT 이름
  FROM 직원
 WHERE 급여 ( ㉠ ) (SELECT 급여 FROM 직원
                    WHERE 부서 = '영업')
 ORDER BY 이름;

[결과]

이름
문수
신혜
1= ANY
2> ALL
3NOT IN
4< ANY
정답 및 해설

영업 급여는 300과 450이고 결과는 이 둘보다 모두 큰 문수(500)·신혜(600)이므로 > ALL 이 맞다.

> ALL 은 서브쿼리의 최댓값을 넘는 행, < ANY 는 최댓값 미만인 행을 고른다.

  • < ANY 는 450 미만인 강민·배진·오현을 반환한다.
  • = ANY 는 IN 과 같아 급여가 300 또는 450 인 강민·고은·오현을 반환한다.
  • NOT IN 은 300·450 이 아닌 문수·배진·신혜를 반환한다.
문제 31
다음 SQL 의 결과 행 수와 마지막 행의 매출액을 순서대로 나열한 것은?

[본점실적]

직원명금액
김하늘300
이서진150
박구름90
최별200

[지점실적]

사원명실적
이서진150
정노을250
한빛80
SELECT 직원명 AS 이름, 금액 AS 매출액
  FROM 본점실적
 WHERE 금액 >= 100
UNION
SELECT 사원명, 실적
  FROM 지점실적
 ORDER BY 매출액 DESC;
16, 80
25, 80
36, 90
44, 150
정답 및 해설

WHERE 는 첫 SELECT 에만 걸려 박구름만 빠지고, UNION 이 중복된 이서진 150 을 한 번만 남겨 5행이며 마지막 행은 한빛 80이다.

ORDER BY 는 집합 연산 맨 끝에 한 번만 쓰며 전체 결과에 적용되고, 열 이름은 첫 SELECT 의 별칭을 따르므로 매출액으로 정렬할 수 있다.

  • 6행은 UNION ALL 처럼 중복 행을 남긴 경우이다.
  • 4행·150 은 WHERE 가 두 번째 SELECT 에도 걸린다고 본 경우이다.
  • 6행·90 은 WHERE 조건을 무시해 박구름까지 남긴 경우이다.
문제 32
집합 연산자(Set Operator)에 대한 설명으로 옳은 것은?
1INTERSECT 는 공통 행을 중복 포함해 반환한다
2UNION ALL 은 중복 행을 그대로 남긴다
3열 이름은 마지막 SELECT 기준으로 정해진다
4UNION ALL 은 정렬된 결과를 보장한다
정답 및 해설

UNION ALL 은 두 결과를 이어 붙이기만 하므로 중복 행이 그대로 남고, 행 수는 두 결과 행 수의 합과 같다.

중복 제거 과정이 없어 UNION 보다 빠르며, 결과 열 이름은 첫 번째 SELECT 를 따른다.

  • 결과 열 이름과 별칭은 마지막이 아니라 첫 SELECT 에서 정해진다.
  • 정렬이 필요하면 맨 끝에 ORDER BY 를 써야 하며 UNION ALL 은 순서를 보장하지 않는다.
  • INTERSECT 는 공통 행을 중복 없이 반환한다.
문제 33
다음 [결과]를 얻기 위해 ㉠에 들어갈 구문으로 옳은 것은?

[매출]

지점분기금액
강남1Q100
강남2Q150
판교1Q200
판교2Q120
수원1Q80
SELECT 지점, SUM(금액) AS 합계,
       GROUPING(지점) AS G
  FROM 매출
 GROUP BY ㉠
 ORDER BY G, 지점;

[결과]

지점합계G
강남2500
수원800
판교3200
(null)6501
1지점, 분기
2GROUPING SETS (지점, ())
3CUBE (지점, 분기)
4ROLLUP (지점, 분기)
정답 및 해설

결과는 분기 없이 지점별 합계 3행과 G 가 1인 총계 1행뿐이므로 GROUPING SETS (지점, ()) 가 맞다.

GROUPING 함수는 해당 열이 집계로 묶여 NULL 이 된 행에서 1, 그 외에는 0을 돌려주며, 괄호 빈 집합 () 이 총계를 만든다.

  • ROLLUP (지점, 분기) 는 지점·분기별 상세 5행과 지점 소계까지 더해 9행이 된다.
  • CUBE (지점, 분기) 는 여기에 분기별 소계까지 더해진다.
  • 지점, 분기 로 묶으면 상세 5행만 나오고 총계가 없다.
문제 34
다음 ㉠과 ㉡의 결과 행 수를 순서대로 나열한 것은?

[배송실적]

지역월건수
서울1월40
서울2월35
부산1월20
대구1월15
대구2월25

㉠

SELECT 지역, 월, SUM(건수) FROM 배송실적
 GROUP BY ROLLUP (지역, 월);

㉡

SELECT 지역, 월, SUM(건수) FROM 배송실적
 GROUP BY ROLLUP (월, 지역);
111, 11
28, 9
39, 9
49, 8
정답 및 해설

ROLLUP 은 (전체 인수) → (첫 인수) → () 순으로 집계하므로 ㉠은 상세 5 + 지역 소계 3 + 총계 1 = 9행, ㉡은 상세 5 + 월 소계 2 + 총계 1 = 8행이다.

ROLLUP 은 인수 순서가 바뀌면 소계 대상이 바뀌어 결과가 달라지며, 순서와 무관한 것은 CUBE 이다.

  • 8, 9 는 소계가 마지막 인수 기준이라고 거꾸로 본 경우이다.
  • 9, 9 는 인수 순서가 결과에 영향이 없다고 본 경우이다.
  • 11, 11 은 두 방향 소계를 모두 만드는 CUBE 의 결과이다.
문제 35
다음 SQL 의 결과 전체 행 수와, 그중 직급 열이 NULL 인 행 수를 순서대로 나열한 것은?

[직원]

사번부서직급급여
1영업과장400
2영업대리300
3영업(null)250
4개발과장450
5개발대리350
6개발대리330
7인사(null)280
SELECT 부서, 직급, SUM(급여) AS 합계
  FROM 직원
 GROUP BY ROLLUP (부서, 직급);
111, 5
210, 6
39, 4
410, 4
정답 및 해설

상세 그룹은 영업 3(과장·대리·NULL)·개발 2·인사 1 로 6행이고, 여기에 부서 소계 3행과 총계 1행이 더해져 10행이다.

직급이 NULL 인 행은 소계·총계 4행에 원래 직급이 NULL 인 상세 그룹 2행이 더해져 6행이며, 둘을 구별하려면 GROUPING 함수를 쓴다.

  • 10, 4 는 원래 NULL 인 상세 그룹을 소계로만 본 경우이다.
  • 9, 4 는 총계 1행과 원래 NULL 인 상세 그룹 2행을 함께 빠뜨린 수이다.
  • 11, 5 는 행 수와 NULL 행 수 모두 실제 10행·6행과 맞지 않는다.
문제 36
다음 [결과]를 얻기 위해 ㉠에 들어갈 함수로 옳은 것은?

[월매출]

월금액
1100
2300
3200
4400
5150
SELECT 월, 금액,
       ( ㉠ ) OVER (ORDER BY 월) AS 비교값
  FROM 월매출
 ORDER BY 월;

[결과]

월금액비교값
1100200
2300400
3200150
4400(null)
5150(null)
1FIRST_VALUE(금액)
2MAX(금액)
3LEAD(금액, 2)
4LAG(금액)
정답 및 해설

각 행의 비교값이 두 달 뒤 금액이고 마지막 두 행이 NULL 이므로 LEAD(금액, 2) 이다.

LEAD 는 정렬 기준으로 뒤쪽 n번째 행 값을 가져오며, 가져올 행이 없으면 기본값을 주지 않는 한 NULL 이다.

  • LAG(금액) 는 직전 달 값이라 첫 행이 NULL 이고 100·300·200·400 이 이어진다.
  • FIRST_VALUE(금액) 는 모든 행에 첫 달 금액 100 을 준다.
  • MAX(금액) 는 기본 프레임에서 누적 최댓값 100·300·300·400·400 을 준다.
문제 37
다음 SQL 결과에서 이름이 다솜인 행의 PR, NT 값을 순서대로 나열한 것은?

[영업사원]

이름매출
가온200
나래250
다솜300
라온350
마루400
SELECT 이름,
       PERCENT_RANK() OVER (ORDER BY 매출) AS PR,
       NTILE(2) OVER (ORDER BY 매출) AS NT
  FROM 영업사원
 ORDER BY 매출;
10.4, 2
20.6, 1
30.5, 2
40.5, 1
정답 및 해설

다솜은 5행 중 3위이므로 PERCENT_RANK 는 (3−1)/(5−1)=0.5 이고, NTILE(2) 는 3행·2행으로 나누어 다솜이 첫 그룹이라 1이다.

NTILE 은 행 수가 나누어떨어지지 않으면 앞 그룹부터 한 행씩 더 배정한다.

  • NT 를 2로 본 것은 나머지 행을 뒤 그룹에 준다고 착각한 경우이다.
  • 0.6 은 자신 이하 행 비율인 CUME_DIST 값(3/5)이다.
  • 0.4 는 분모를 행 수 5로 잡은 계산으로 PERCENT_RANK 식이 아니다.
문제 38
다음 ㉠의 결과 행 수, ㉡의 결과 행 수, ㉡에서 부서가 개발인 행의 합계 값을 순서대로 나열한 것은?

[직원]

사번부서급여
1영업300
2영업200
3개발400
4개발350
5개발240
6(null)280
7인사330

㉠

SELECT 부서, SUM(급여) AS 합계
  FROM 직원
 WHERE 급여 >= 250
 GROUP BY 부서;

㉡

SELECT 부서, SUM(급여) OVER (PARTITION BY 부서) AS 합계
  FROM 직원
 WHERE 급여 >= 250;
13, 5, 750
24, 5, 750
34, 7, 990
44, 5, 990
정답 및 해설

WHERE 로 급여 200·240 두 행이 빠진 뒤 ㉠은 영업·개발·인사·NULL 의 4개 그룹, ㉡은 남은 5행을 그대로 돌려주고 개발 행 합계는 400+350=750이다.

윈도우 함수는 WHERE·GROUP BY 처리 뒤에 계산되며 행을 줄이지 않고, 부서가 NULL 인 행도 하나의 그룹·파티션이 된다.

  • 3개 그룹은 부서가 NULL 인 행이 그룹에서 빠진다고 본 경우이다.
  • 7행·990 은 윈도우 함수가 WHERE 보다 먼저 계산된다고 본 경우이다.
  • 5행이면서 990 은 합계에만 WHERE 가 적용되지 않는다고 본 모순된 계산이다.
문제 39
다음 SQL 결과에서 회원명이 마로인 행의 R, DR, RN 값을 순서대로 나열한 것은?

[회원]

회원명포인트
가람90
나비85
다인85
라임80
마로80
바름80
사랑70
SELECT 회원명,
       RANK() OVER (ORDER BY 포인트 DESC) AS R,
       DENSE_RANK() OVER (ORDER BY 포인트 DESC) AS DR,
       ROW_NUMBER() OVER (ORDER BY 포인트 DESC, 회원명) AS RN
  FROM 회원
 ORDER BY RN;
14, 3, 4
24, 4, 5
34, 3, 5
45, 3, 5
정답 및 해설

마로(80)는 앞에 90·85·85 세 행이 있어 RANK 4, 서로 다른 값으로는 세 번째라 DENSE_RANK 3 이다.

ROW_NUMBER 는 포인트가 같으면 회원명 순으로 라임 다음 번호를 받아 5가 되며, 동점이어도 같은 번호를 주지 않는다.

  • DR 을 4로 본 것은 DENSE_RANK 도 동점 수만큼 건너뛴다고 착각한 경우이다.
  • R 을 5로 본 것은 RANK 와 ROW_NUMBER 를 혼동한 경우이다.
  • RN 을 4로 본 것은 보조 정렬인 회원명 순서를 무시한 경우이다.
문제 40
Oracle(12c 이상)에서 다음 SQL 을 실행한 결과로 옳은 것은?

[상품]

상품코드분류판매량
P1식품120
P2생활(null)
P3식품90
P4가전150
P5생활90
P6가전90
P7식품60
SELECT 상품코드, 판매량
  FROM (SELECT 상품코드, 판매량
          FROM 상품
         ORDER BY 판매량 DESC NULLS LAST
         FETCH FIRST 3 ROWS WITH TIES) T
 ORDER BY 판매량 DESC, 상품코드;
1
상품코드판매량
P390
P590
P690
P760
2
상품코드판매량
P2(null)
P4150
P1120
3
상품코드판매량
P4150
P1120
P390
P590
P690
4
상품코드판매량
P4150
P1120
P390
정답 및 해설

WITH TIES 는 세 번째 행의 판매량 90 과 같은 값을 가진 행을 모두 함께 가져오므로 P4·P1 에 90 인 P3·P5·P6 이 더해져 5행이 된다.

NULLS LAST 로 판매량 NULL 인 P2 는 맨 뒤로 밀려 제외되고, 바깥 ORDER BY 가 동점 행을 상품코드 순으로 정렬한다.

  • 3행만 나온 표는 ROWS ONLY 처럼 동점 행을 버린 경우다.
  • P2 가 들어간 표는 NULLS LAST 를 무시하고 Oracle 기본 정렬을 적용한 경우다.
  • P7 이 들어간 표는 판매량을 오름차순으로 정렬해 뽑은 경우다.
문제 41
Oracle(12c 이상)에서 매출 상위 3개 지점을 구하려 한다. 매출에 NULL 과 동점이 없을 때, 의도한 결과를 보장하지 않는 SQL은?
㉠ SELECT 지점명, 매출
     FROM (SELECT 지점명, 매출 FROM 지점 ORDER BY 매출 DESC)
    WHERE ROWNUM <= 3;

㉡ SELECT 지점명, 매출
     FROM 지점
    WHERE ROWNUM <= 3
    ORDER BY 매출 DESC;

㉢ SELECT 지점명, 매출
     FROM 지점
    ORDER BY 매출 DESC
    FETCH FIRST 3 ROWS ONLY;

㉣ SELECT 지점명, 매출
     FROM (SELECT 지점명, 매출,
                  ROW_NUMBER() OVER (ORDER BY 매출 DESC) AS RN
             FROM 지점)
    WHERE RN <= 3;
1㉠
2㉣
3㉢
4㉡
정답 및 해설

ROWNUM 은 WHERE 단계에서 행을 읽는 순서대로 붙고 ORDER BY 는 그 뒤에 실행되므로, ㉡은 먼저 걸러진 임의의 3행을 정렬할 뿐이다.

상위 N 개를 정확히 얻으려면 정렬을 인라인 뷰 안에서 끝낸 뒤 바깥에서 ROWNUM 을 걸어야 한다.

  • ㉠은 인라인 뷰에서 먼저 정렬한 뒤 ROWNUM 으로 자르므로 올바르다.
  • ㉢의 FETCH FIRST 는 ORDER BY 이후에 적용되는 12c 이상 표준 구문이다.
  • ㉣은 ROW_NUMBER 로 순번을 매긴 뒤 바깥에서 3 이하만 고르므로 올바르다.
문제 42
Oracle 기준으로 다음 SQL 결과의 다섯 번째 행에 나오는 이름은?

[직원]

사번이름상사사번
1한지민(null)
2윤서준1
3강하린1
4서도윤2
5김태오3
6남유진2
7노을5
SELECT LEVEL, 이름
  FROM 직원
 START WITH 상사사번 IS NULL
 CONNECT BY PRIOR 사번 = 상사사번
 ORDER SIBLINGS BY 이름;
1남유진
2서도윤
3강하린
4윤서준
정답 및 해설

계층은 깊이 우선으로 펼쳐지고 ORDER SIBLINGS BY 가 같은 상사를 둔 형제끼리만 이름순으로 정렬하므로, 순서는 한지민·강하린·김태오·노을·윤서준·남유진·서도윤이다.

강하린이 윤서준보다 앞서므로 강하린의 하위 전체(김태오·노을)가 먼저 나온 뒤 윤서준 가지로 넘어간다.

  • 서도윤은 계층을 무시하고 전체를 이름순으로 정렬한 ORDER BY 의 결과이다.
  • 남유진은 레벨별로 한 층씩 나열한다고 본 결과이다.
  • 강하린은 형제를 사번순으로 펼친다고 본 결과이다.
문제 43
다음 SQL 에 대한 설명으로 옳은 것은?

[직원]

사번이름관리자사번
1정은(null)
2하람1
3류진1
4백설2
SELECT A.이름 AS 직원, B.이름 AS 관리자
  FROM 직원 A
  JOIN 직원 B
    ON A.관리자사번 = B.사번;
1관리자가 없는 정은도 결과에 나온다
2관리자 열에는 상사의 이름이 나온다
3결과 행 수는 직원 수와 항상 같다
4같은 테이블은 별칭 없이 두 번 쓴다
정답 및 해설

셀프 조인에서 A 는 직원, B 는 관리자 역할을 하며, A 의 관리자사번과 B 의 사번이 같은 행을 연결하므로 관리자 열에는 상사의 이름이 나온다.

결과는 하람-정은, 류진-정은, 백설-하람의 3행이다.

  • INNER 조인이라 관리자사번이 NULL 인 정은은 결과에서 빠진다.
  • 같은 테이블을 두 번 쓰려면 반드시 서로 다른 별칭으로 구분해야 한다.
  • 정은이 빠져 3행이므로 직원 수 4와 다르다.
문제 44
다음 SQL 결과에서 NULL 로 표시되는 셀은 모두 몇 개인가?

[배송]

배송번호지역상태
1서울완료
2서울지연
3서울완료
4부산완료
5부산반송
6대구지연
7대구지연
8(null)완료
SELECT 지역,
       SUM(CASE WHEN 상태 = '완료' THEN 1 ELSE 0 END) AS 완료,
       COUNT(CASE WHEN 상태 = '반송' THEN 1 END) AS 반송,
       SUM(CASE WHEN 상태 = '지연' THEN 1 END) AS 지연
  FROM 배송
 GROUP BY 지역
 ORDER BY 지역;
13
25
31
42
정답 및 해설

지연 열은 ELSE 가 없어 지연 건이 없는 부산과 지역 NULL 그룹에서 SUM 이 NULL 이 되고, 지역 NULL 그룹의 지역 셀까지 더해 NULL 셀은 3개이다.

반송 열은 COUNT 라 해당 건이 없으면 NULL 이 아닌 0이 되고, 완료 열은 ELSE 0 덕분에 모두 숫자이다.

  • 1개는 지역이 NULL 인 행도 하나의 그룹이 된다는 점을 놓친 경우이다.
  • 2개는 NULL 그룹의 지역 셀이나 지연 셀 중 하나만 센 경우이다.
  • 5개는 COUNT 도 대상이 없으면 NULL 을 준다고 보고 지역 셀을 빠뜨린 경우이다.
문제 45
Oracle 기준(대소문자 구분)으로 다음 SQL 의 결과 값은?

[회원]

회원ID
kim01
Lee22
park7
choi123
jung_05
han99
yoo 10
ab12
SELECT COUNT(*)
  FROM 회원
 WHERE REGEXP_LIKE(회원ID, '^[[:lower:]]+[[:digit:]]{2}$');
15
24
33
42
정답 및 해설

소문자 1자 이상 뒤에 숫자가 정확히 2자로 끝나야 하므로 kim01·han99·ab12 의 3건만 일치한다.

[[:lower:]] 는 소문자, [[:digit:]] 는 숫자를 뜻하고, ^ 와 $ 가 문자열 전체를 감싸 중간 일치를 허용하지 않는다.

  • Lee22 는 첫 글자가 대문자라 [[:lower:]] 와 맞지 않는다.
  • park7 은 숫자가 1자, choi123 은 3자라 {2} 와 $ 조건에 걸린다.
  • jung_05 와 yoo 10 은 밑줄과 공백이 문자 클래스에 없어 제외된다.
문제 46
다음 MERGE 문에 대한 설명으로 옳은 것은?
MERGE INTO 회원 T
USING 신규회원 S
   ON (T.회원번호 = S.회원번호)
 WHEN MATCHED THEN
   UPDATE SET T.등급 = S.등급
 WHEN NOT MATCHED THEN
   INSERT (회원번호, 이름, 등급)
   VALUES (S.회원번호, S.이름, S.등급);
1일치하지 않는 원본 행은 새로 삽입된다
2MERGE 는 DDL 이므로 자동 커밋된다
3원본에 없는 대상 행은 삭제된다
4일치하는 대상 행은 삭제 후 재입력된다
정답 및 해설

MERGE 는 ON 조건으로 원본(신규회원)과 대상(회원)을 비교해, 짝이 없는 원본 행은 WHEN NOT MATCHED 절에 따라 회원에 새로 삽입한다.

짝이 있는 행은 WHEN MATCHED 절에 따라 등급만 원본 값으로 갱신된다.

  • 일치하는 행은 삭제 없이 UPDATE 로 값만 바뀐다.
  • 원본에 없는 대상 행은 이 문장에서 아무 영향을 받지 않는다.
  • MERGE 는 DML 이라 COMMIT 이나 ROLLBACK 으로 확정·취소해야 한다.
문제 47
격리 수준이 READ UNCOMMITTED 일 때 다음 상황에서 세션 B 가 겪은 현상은?
[세션 A] UPDATE 지점 SET 매출 = 900 WHERE 지점코드 = 'A';
         -- 아직 COMMIT 하지 않음
[세션 B] SELECT 매출 FROM 지점 WHERE 지점코드 = 'A';
         -- 900 이 조회됨
[세션 A] ROLLBACK;
1Dirty Read
2Phantom Read
3Non-Repeatable Read
4Lost Update
정답 및 해설

세션 B 는 커밋되지 않았다가 결국 롤백된 900 을 읽었으므로 Dirty Read 이다.

READ COMMITTED 이상에서는 커밋된 데이터만 읽어 이 현상을 막으며, Oracle 은 변경 전 값을 보여 주는 읽기 일관성으로 처리한다.

  • Non-Repeatable Read 는 같은 행을 두 번 읽는 사이 커밋된 변경으로 값이 달라지는 현상이다.
  • Phantom Read 는 같은 조건 재조회 시 새 행이 나타나는 현상이다.
  • Lost Update 는 두 트랜잭션의 갱신이 겹쳐 한쪽 변경이 사라지는 현상이다.
문제 48
다음과 같이 정의된 상태에서 회원 테이블의 한 회원을 삭제하면, 그 회원이 쓴 게시글은 어떻게 되는가?
CREATE TABLE 게시글 (
  글번호     INT PRIMARY KEY,
  작성자번호 INT,
  CONSTRAINT FK_작성자 FOREIGN KEY (작성자번호)
    REFERENCES 회원 (회원번호)
    ON DELETE SET NULL
);
1작성자번호가 기본값으로 바뀐다
2해당 게시글도 함께 삭제된다
3작성자번호가 NULL 로 바뀐다
4자식 행 때문에 삭제가 거부된다
정답 및 해설

외래키에 ON DELETE SET NULL 을 지정했으므로 부모 회원이 삭제되면 해당 게시글의 작성자번호가 NULL 로 바뀌고 게시글 행은 남는다.

작성자번호 열에 NOT NULL 제약이 없어야 이 동작이 가능하며, 옵션을 생략하면 참조되는 부모 행 삭제가 거부된다.

  • 게시글까지 지우는 것은 ON DELETE CASCADE 의 동작이다.
  • 삭제 거부는 옵션을 생략하거나 RESTRICT 계열을 지정한 경우이다.
  • 기본값으로 바꾸는 것은 SET DEFAULT 이며 이 정의와 다르다.
문제 49
Oracle 에서 큰따옴표 없이 실행할 때 오류가 발생하는 CREATE TABLE 문은?
1CREATE TABLE 2ND_ZONE (ZONE_CD CHAR(3))
2CREATE TABLE BR$HIST (OPEN_DT DATE)
3CREATE TABLE DLV_LOG (MEMO VARCHAR2(200))
4CREATE TABLE MEM_PT (PT NUMBER(7,2))
정답 및 해설

테이블명은 문자로 시작해야 하므로 숫자로 시작하는 2ND_ZONE 은 큰따옴표 없이 쓰면 오류가 발생한다.

이름에는 문자·숫자와 _ $ # 만 쓸 수 있고, 예약어나 같은 스키마 안 중복 이름도 허용되지 않는다.

  • DLV_LOG 는 밑줄을 포함한 올바른 이름이고 VARCHAR2(200) 도 유효하다.
  • BR$HIST 처럼 $ 는 이름 중간에 쓸 수 있고 DATE 타입도 정상이다.
  • NUMBER(7,2) 는 전체 7자리 중 소수 2자리를 뜻하는 유효한 타입이다.
문제 50
Oracle 에서 오브젝트 권한(Object Privilege)을 부여하는 문장은?
1GRANT CREATE ANY VIEW TO CHOI
2GRANT CREATE TABLE TO KIM
3GRANT CREATE SESSION TO PARK
4GRANT UPDATE ON 직원 TO LEE
정답 및 해설

오브젝트 권한은 특정 객체에 대한 SELECT·INSERT·UPDATE·DELETE 등의 권한으로, ON 직원 처럼 대상 객체를 지정해 부여한다.

CREATE TABLE·CREATE SESSION 처럼 데이터베이스 전체에서 특정 작업을 할 수 있게 하는 것은 시스템 권한이다.

  • CREATE TABLE 은 자기 스키마에 테이블을 만들 수 있는 시스템 권한이다.
  • CREATE SESSION 은 접속을 허용하는 시스템 권한이다.
  • CREATE ANY VIEW 는 다른 스키마에도 뷰를 만들 수 있는 시스템 권한이다.