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

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

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

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

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

총 50문항 · 객관식 · 정답·해설 포함
문제 1
보험 계약 시스템을 운영하던 중 [계약] 엔터티의 구조를 바꾸자, 이를 쓰는 수십 개의 프로그램과 화면을 함께 고쳐야 했다. 데이터 모델링이 중요한 이유 중 이 상황과 가장 관련 깊은 것은?
1데이터 독립성(Independence)
2간결한 표현(Conciseness)
3파급효과(Leverage)
4데이터 품질(Data Quality)
정답 및 해설

데이터 모델은 많은 프로그램이 공통으로 기대는 기반이라 설계 후반이나 운영 중에 바꾸면 영향이 넓게 퍼지는데, 이를 파급효과(Leverage)라 한다.

그래서 모델은 개발 초기에 충분히 검토해 확정해야 하며, 늦게 바꿀수록 수정 비용이 커진다.

  • 간결한 표현은 모델이 업무 요구를 정확하고 간결하게 전달하는 역할을 말한다.
  • 데이터 품질은 중복·비일관 데이터로 생기는 정확성 문제를 가리킨다.
  • 데이터 독립성은 3단계 스키마 구조의 성질로, 모델링이 중요한 이유(파급효과·간결한 표현·데이터 품질)에 들지 않는다.
문제 2
보험상품처럼 물리적인 형태는 없지만 관리할 가치가 있는 개념적 정보를 나타내는 엔터티를 유무형에 따라 분류하면 무엇인가?
1사건 엔터티
2개념 엔터티
3행위 엔터티
4유형 엔터티
정답 및 해설

유무형에 따른 분류에서 형태는 없지만 관리해야 할 개념적 정보는 개념 엔터티로, 보험상품·조직 등이 해당한다.

같은 기준의 다른 분류로 물리적 형태가 있는 유형 엔터티와 업무 수행 중 발생하는 사건 엔터티가 있다.

  • 유형 엔터티는 고객·설계사처럼 물리적 형태가 있는 대상이다.
  • 사건 엔터티는 청구 접수·납입처럼 업무 중 발생하는 대상이다.
  • 행위 엔터티는 발생 시점에 따른 분류라 기준이 다르다.
문제 3
계약 엔터티의 속성 중 파생 속성(Derived Attribute)에 해당하는 것은?
1납입 내역을 합산한 누적납입액
2청약서에 적힌 계약 시작일
3업무 편의로 부여한 상태코드
4가입 시 정한 월 보험료
정답 및 해설

다른 속성 값을 계산·집계해서 얻는 속성이 파생 속성이며, 납입 내역을 합한 누적납입액이 이에 해당한다.

파생 속성은 원천 값이 바뀔 때마다 함께 갱신해야 하므로 데이터 정합성 관리를 위해 꼭 필요한 경우에만 둔다.

  • 청약서에 적힌 계약 시작일은 업무에서 나온 기본 속성이다.
  • 업무 편의로 부여한 상태코드는 설계 속성이다.
  • 가입 시 정한 월 보험료는 업무에서 나온 기본 속성이다.
문제 4
다음 ERD의 계약특약 엔터티에 대한 설명으로 옳은 것은?

그림

1계약과 특약의 M:N 관계를 해소한다
2주식별자는 특약코드 하나뿐이다
3계약 없이도 인스턴스가 생성된다
4특약의 상위 유형을 나타낸 엔터티다
정답 및 해설

계약 하나에 여러 특약이, 특약 하나가 여러 계약에 붙는 M:N 관계를 계약특약이라는 교차 엔터티로 1:N 두 개로 풀어낸 모델이다.

두 관계가 모두 식별 관계라 계약번호와 특약코드를 함께 상속받아 복합 주식별자를 이룬다.

  • 상위 유형(슈퍼타입)이 아니라 두 엔터티 사이의 교차 엔터티다.
  • 식별 관계로 계약번호를 상속하므로 계약 없이 생성될 수 없다.
  • 주식별자는 계약번호와 특약코드의 조합이다.
문제 5
다음 ERD에서 청구의 주식별자를 사고접수번호로 정하였다. 사고접수번호는 자동차 사고 청구에만 외부 기관이 발급하고, 질병 청구는 이 값 없이 접수된다. 이 설계가 어기는 주식별자 특성은?

그림

1존재성
2유일성
3불변성
4최소성
정답 및 해설

주식별자는 모든 인스턴스에 반드시 값이 있어야 하는 존재성을 갖춰야 하는데, 질병 청구는 사고접수번호가 비어 있으므로 이를 어긴다.

이런 경우 모든 청구에 값이 생기는 청구번호 같은 속성을 주식별자로 두고 사고접수번호는 일반 속성으로 내린다.

  • 발급된 사고접수번호끼리는 겹치지 않아 유일성 문제는 아니다.
  • 속성 하나로 이루어져 최소성은 충족한다.
  • 한 번 발급된 번호가 바뀌는 상황은 제시되지 않았다.
문제 6
다음 릴레이션에 대한 설명으로 옳은 것은?
상담배정(고객번호, 상담분야, 상담사)
후보키: (고객번호, 상담분야), (고객번호, 상담사)
함수 종속:
  (고객번호, 상담분야) → 상담사
  상담사 → 상담분야   -- 상담사는 한 분야만 맡는다
1부분 종속이 있어 2NF 위반이다
2상담사의 이행 종속으로 3NF 위반이다
33NF는 만족하나 BCNF는 위반이다
4BCNF 분해 시 모든 종속이 보존된다
정답 및 해설

결정자 상담사가 후보키가 아니므로 BCNF 위반이지만, 종속되는 상담분야가 후보키의 일부(주요 속성)라 3NF는 만족한다.

모든 속성이 어느 후보키에 속해 있어 키가 아닌 속성의 부분·이행 종속이 생기지 않는다.

  • 부분 종속은 키가 아닌 속성에 대한 것이라 2NF 위반이 아니다.
  • 상담분야는 주요 속성이라 이행 종속에 의한 3NF 위반이 아니다.
  • (상담사, 상담분야)로 분해하면 (고객번호, 상담분야) → 상담사 종속이 보존되지 않는다.
문제 7
고객과 계약처럼 1:N 관계로 연결된 두 테이블을 조인하는 원리에 대한 설명으로 옳은 것은?
1조인 조건은 기본키끼리만 쓸 수 있다
21:N 관계의 외래키는 1 쪽에 둔다
3자식 외래키와 부모 기본키 값을 비교한다
4두 테이블의 열 이름이 같아야 한다
정답 및 해설

모델의 관계는 자식 테이블의 외래키(FK) 값과 부모 테이블의 기본키(PK) 값이 같은 행을 잇는 조인으로 구현된다.

고객-계약이라면 계약.고객번호 = 고객.고객번호 조건으로 각 계약에 그 계약자 정보를 붙인다.

  • 열 이름이 달라도 값의 의미가 같으면 조인할 수 있다.
  • 1:N 관계의 외래키는 N 쪽(자식) 테이블에 둔다.
  • 조인 조건은 PK 가 아닌 일반 열끼리도 쓸 수 있다.
문제 8
보험 업무 모델에서 하나의 트랜잭션으로 묶어 원자성을 보장해야 하는 작업으로 가장 적절한 것은? (지급이 생기면 해당 청구는 반드시 지급완료 상태가 되어야 하며, 고객은 계약 없이도 등록할 수 있다)
1특약 상품 등록과 설계사 정보 수정
2신규 고객 등록과 이후의 첫 계약
3청구 내역 조회와 화면 출력 기록
4보험금 지급 입력과 청구 상태 변경
정답 및 해설

지급 내역 입력과 청구 상태 변경은 한쪽만 반영되면 데이터가 어긋나므로 하나의 트랜잭션으로 묶어 원자성을 보장해야 한다.

모델에서는 이처럼 함께 생겨야 하는 엔터티·속성 변경을 필수 관계로 표현해 같은 작업 단위임을 드러낸다.

  • 고객은 계약 없이 등록되는 선택 관계라 따로 처리해도 된다.
  • 특약 등록과 설계사 수정은 서로 관련 없는 별개 작업이다.
  • 조회와 출력 기록은 함께 확정해야 할 데이터 변경이 아니다.
문제 9
NULL 에 대한 설명으로 옳은 것은?
1NULL에 0을 더하면 0이다
2COUNT(열)은 NULL도 센다
3NULL끼리 = 비교는 UNKNOWN이다
4MAX는 NULL을 최댓값으로 본다
정답 및 해설

NULL 은 알 수 없는 값이라 NULL 끼리 = 로 비교해도 TRUE 가 아니라 UNKNOWN이 되며, 그래서 IS NULL 로 검사해야 한다.

그룹 함수는 NULL 을 제외하고 계산하고, 산술 연산에 NULL 이 섞이면 결과도 NULL 이다.

  • COUNT(열)은 NULL 을 세지 않고 COUNT(*)만 모든 행을 센다.
  • NULL 에 어떤 수를 더해도 결과는 NULL 이다.
  • MAX 는 NULL 을 무시하고 나머지 값 중 최댓값을 구한다.
문제 10
다음 청구 테이블은 청구ID(일련번호)를 기본키로 쓴다. 업무 규칙상 같은 계약·같은 사고일자·같은 청구차수의 청구는 한 건만 있어야 할 때, 이 테이블에 대한 설명으로 옳은 것은?

[청구]

청구ID계약번호사고일자청구차수청구금액
1K012026-03-021120
2K022026-03-02180
3K012026-03-02260
4K032026-03-101200
5K012026-04-15190
6K032026-03-101200
1업무상 같은 청구가 중복 입력돼 있다
2사고일자·차수 UNIQUE로 충분하다
3PK가 있어 중복 청구는 생길 수 없다
4계약번호 UNIQUE면 중복만 막는다
정답 및 해설

청구ID 4와 6은 계약 K03·사고일자 03-10·차수 1이 같은 업무상 중복인데, 인조식별자인 청구ID 값만 달라 기본키로는 걸러지지 않았다.

막으려면 (계약번호, 사고일자, 청구차수) 세 속성 조합 전체에 UNIQUE 제약을 걸어야 한다.

  • 인조식별자 PK 는 값만 다르면 같은 업무 데이터도 받아들인다.
  • 계약번호만 UNIQUE면 K01 의 정상적인 여러 청구까지 막힌다.
  • 사고일자·차수만 UNIQUE면 K01 과 K02 의 정상 청구가 충돌한다.
문제 11
[계약] 테이블의 납입주기 열에는 '월납', '분기납', '연납' 중 하나만 들어갈 수 있다. 이처럼 한 속성이 가질 수 있는 값들의 집합을 관계형 모델에서 무엇이라 하는가?
1인스턴스(Instance)
2튜플(Tuple)
3도메인(Domain)
4스키마(Schema)
정답 및 해설

관계형 모델에서 한 속성이 취할 수 있는 원자값들의 집합을 도메인(Domain)이라 하므로 납입주기의 허용값 집합은 도메인이다.

실제 테이블에서는 데이터 타입과 CHECK 제약 등으로 도메인을 구현해 범위 밖 값이 들어오지 못하게 한다.

  • 튜플은 테이블의 한 행, 즉 계약 한 건의 값 묶음이다.
  • 스키마는 테이블 이름과 속성 구성 같은 구조 정의이다.
  • 인스턴스는 어느 시점에 테이블에 실제로 들어 있는 행들의 집합이다.
문제 12
SELECT 절의 열 별칭(Alias)에 대한 설명으로 옳은 것은?
1별칭은 작은따옴표로 감싸야 한다
2별칭 앞에는 반드시 AS를 써야 한다
3별칭은 WHERE 절에서 쓸 수 있다
4공백이 든 별칭은 큰따옴표로 감싼다
정답 및 해설

별칭에 공백·특수문자가 있거나 대소문자를 그대로 두려면 큰따옴표로 감싸야 한다. 예: 월보험료 * 12 AS "연간 보험료"

별칭 앞의 AS 는 생략할 수 있고, 별칭은 SELECT 이후에 처리되는 ORDER BY 에서 쓸 수 있다.

  • AS 는 선택 사항이라 생략해도 된다.
  • WHERE 는 SELECT 보다 먼저 처리되어 별칭을 쓸 수 없다.
  • 작은따옴표는 문자열 상수를 나타낼 때 쓴다.
문제 13
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호설계사월보험료
1한결60
2한결40
3한결100
4서윤70
5서윤80
6지호30
7지호150
8태오50
9태오120
10서윤45
SELECT 설계사, SUM(월보험료) AS 합계, COUNT(*) AS 건수
  FROM 계약
 WHERE 월보험료 >= 50
 GROUP BY 설계사
HAVING COUNT(*) >= 2
 ORDER BY 합계 DESC;
1
설계사합계건수
서윤1502
한결1602
태오1702
2
설계사합계건수
태오1702
한결1603
서윤1503
3
설계사합계건수
태오1702
한결1602
서윤1502
4
설계사합계건수
태오1702
한결1602
서윤1502
지호1501
정답 및 해설

WHERE 가 그룹화보다 먼저 실행되어 50 미만인 계약 2·6·10번이 빠진 뒤 집계되므로 태오 170·한결 160·서윤 150(각 2건)이 남는다.

지호는 남은 행이 1건이라 HAVING 에서 제외되고, ORDER BY 는 SELECT 다음에 처리되어 별칭 합계로 내림차순 정렬된다.

  • 지호 행이 남은 표는 HAVING 조건을 적용하지 않은 결과다.
  • 건수가 3인 표는 WHERE 이전 행으로 COUNT 를 센 결과다.
  • 서윤부터 나온 표는 정렬 방향이 오름차순으로 뒤집혔다.
문제 14
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호청구액지급액
101500450
1023000
103200(null)
104(null)0
105150150
SELECT 청구번호,
       COALESCE(NULLIF(지급액, 0), 청구액) AS 기준액,
       CASE WHEN 지급액 > 0 THEN '지급'
            WHEN 지급액 = 0 THEN '부지급'
            ELSE '심사중' END AS 상태
  FROM 청구
 ORDER BY 청구번호;
1
청구번호기준액상태
101450지급
102300부지급
103200심사중
104(null)부지급
105150지급
2
청구번호기준액상태
101450지급
102300부지급
103200부지급
104(null)부지급
105150지급
3
청구번호기준액상태
101450지급
102300부지급
103200심사중
1040부지급
105150지급
4
청구번호기준액상태
101450지급
1020부지급
103200심사중
104(null)부지급
105150지급
정답 및 해설

NULLIF(지급액, 0)은 0을 NULL 로 바꾸므로 102·104는 청구액으로 넘어가 102는 300, 청구액도 NULL 인 104는 COALESCE 결과가 NULL 이다.

103의 지급액 NULL 은 두 WHEN 비교가 모두 UNKNOWN 이라 ELSE 의 심사중이 되고, 104는 지급액이 0이라 부지급이다.

  • 102 기준액이 0인 표는 NULLIF 변환을 빠뜨렸다.
  • 103이 부지급인 표는 NULL 을 0처럼 비교했다.
  • 104 기준액이 0인 표는 모든 인수가 NULL 인 COALESCE 를 0으로 보았다.
문제 15
Oracle 기준으로 다음 SQL 의 실행 결과(건수)는? (상품코드의 데이터 타입은 VARCHAR2 이다)

[계약]

계약번호상품코드
110
2010
3100
40010
51
620
SELECT COUNT(*) AS 건수
  FROM 계약
 WHERE 상품코드 = 10;
13
22
31
44
정답 및 해설

Oracle 은 문자와 숫자를 비교하면 문자 쪽을 숫자로 바꾸는 암시적 형변환을 하므로 조건은 TO_NUMBER(상품코드) = 10 으로 처리된다.

그래서 앞자리 0이 사라진 '10'·'010'·'0010' 세 행이 모두 10과 같아 건수는 3이다.

  • 1건은 문자열 '10' 과 정확히 같은 값만 센 결과다.
  • 2건은 '0010' 을 빠뜨린 결과다.
  • 4건은 '100' 이나 '1' 까지 같다고 본 결과다.
문제 16
다음 결과를 얻기 위해 ( 가 )에 들어갈 식으로 옳은 것은?

[고객청구]

고객번호금액
11500
2300
3(null)
4120
51000
6299
SELECT 고객번호, 금액,
       ( 가 ) AS 등급
  FROM 고객청구
 ORDER BY 고객번호;

㉠ CASE WHEN 금액 >= 300 THEN 'M'
        WHEN 금액 >= 1000 THEN 'H' ELSE 'L' END
㉡ CASE WHEN 금액 >= 1000 THEN 'H'
        WHEN 금액 >= 300 THEN 'M' END
㉢ CASE WHEN 금액 >= 1000 THEN 'H'
        WHEN 금액 >= 300 THEN 'M' ELSE 'L' END
㉣ CASE WHEN 금액 < 300 THEN 'L'
        WHEN 금액 < 1000 THEN 'M' ELSE 'H' END

[원하는 결과]

고객번호금액등급
11500H
2300M
3(null)L
4120L
51000H
6299L
1㉢
2㉡
3㉠
4㉣
정답 및 해설

검색 CASE 는 위에서부터 처음 참이 되는 WHEN 하나만 적용하므로 큰 기준(1000)을 먼저 두고, 나머지는 ELSE 로 L 을 주어야 한다.

금액이 NULL 인 고객 3은 모든 비교가 UNKNOWN 이라 ELSE 의 L 이 되고, 299인 고객 6은 300 미만이라 L 이다.

  • ㉠은 1500도 첫 조건 300 이상에 걸려 H 가 나오지 않는다.
  • ㉡은 ELSE 가 없어 고객 3·4·6의 등급이 NULL 이 된다.
  • ㉣은 NULL 인 고객 3이 ELSE 로 가서 H 가 된다.
문제 17
다음 WHERE 절과 같은 의미의 조건은?
WHERE 상태 = '유지' OR 상태 = '휴면' AND 보험료 >= 10
1(상태='유지' AND 보험료>=10) OR 상태='휴면'
2상태='휴면' AND (상태='유지' OR 보험료>=10)
3상태 IN ('유지','휴면') AND 보험료>=10
4상태='유지' OR (상태='휴면' AND 보험료>=10)
정답 및 해설

논리 연산자는 NOT → AND → OR 순으로 처리되므로 괄호가 없으면 AND 로 이어진 두 조건이 먼저 묶인다.

따라서 유지 상태 계약은 보험료와 관계없이 모두, 휴면 계약은 보험료 10 이상만 남는 A OR (B AND C) 형태가 된다.

  • OR 를 먼저 묶으면 유지 계약에도 보험료 조건이 붙는다.
  • 보험료 조건을 유지 쪽에 붙이면 AND 의 결합 대상을 잘못 본 것이다.
  • 휴면을 바깥으로 빼면 유지 계약이 전부 빠진다.
문제 18
다음 SQL 의 실행 결과로 옳은 것은?

[상품]

상품번호상품코드상품명
1CI_01암진단
2CI02암수술
3CI_뇌혈관
4CIX01입원일당
5XCI_1골절
6CI_A통원
SELECT 상품번호, 상품코드
  FROM 상품
 WHERE 상품코드 LIKE 'CI#_%' ESCAPE '#'
 ORDER BY 상품번호;
1
상품번호상품코드
1CI_01
6CI_A
2
상품번호상품코드
1CI_01
3CI_
6CI_A
3
상품번호상품코드
1CI_01
2CI02
3CI_
4CIX01
6CI_A
4
상품번호상품코드
1CI_01
3CI_
5XCI_1
6CI_A
정답 및 해설

ESCAPE '#' 로 #_ 는 밑줄 문자 그 자체가 되어 패턴은 'CI_' 로 시작하는 값이고, 뒤의 % 는 0글자 이상과 일치하므로 상품번호 1·3·6이 남는다.

CI02·CIX01은 세 번째 글자가 밑줄이 아니라 빠지고, XCI_1은 앞부분이 CI 가 아니라 빠진다.

  • CI02·CIX01이 포함된 표는 #_ 를 한 글자 와일드카드로 본 결과다.
  • CI_ 가 빠진 표는 % 가 한 글자 이상을 요구한다고 본 결과다.
  • XCI_1이 포함된 표는 패턴이 중간에서도 맞는다고 본 결과다.
문제 19
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객번호청구일지급액
1112026-03-28100
2122026-04-02200
3112026-04-05(null)
4132026-04-10150
5122026-04-18(null)
6142026-03-3080
7112026-04-2250
8(null)2026-04-2570
SELECT COUNT(*) AS 전체, COUNT(지급액) AS 지급건,
       COUNT(DISTINCT 고객번호) AS 고객수,
       SUM(지급액) AS 합계
  FROM 청구
 WHERE 청구일 >= DATE '2026-04-01';
1
전체지급건고객수합계
643(null)
2
전체지급건고객수합계
663470
3
전체지급건고객수합계
644470
4
전체지급건고객수합계
643470
정답 및 해설

GROUP BY 가 없으므로 WHERE 로 남은 청구 2·3·4·5·7·8번 6행 전체가 한 그룹이 되어 결과는 1행이다.

지급액 NULL 2건을 뺀 지급건 4, NULL 고객번호를 뺀 고객 11·12·13의 3명, 합계 200+150+50+70=470으로 NULL 은 제외된다.

  • 지급건 6인 표는 COUNT(열)이 NULL 도 센다고 보았다.
  • 고객수 4인 표는 DISTINCT 집계에 NULL 을 포함했다.
  • 합계가 NULL 인 표는 SUM 에 NULL 이 섞이면 NULL 이 된다고 보았다.
문제 20
다음 두 SQL ㉠, ㉡의 결과 행 수를 차례로 나타낸 것은?

[청구]

청구번호고객번호청구액
1C1120
2C130
3C260
4C250
5C390
6C4200
7C4(null)
8C540
9C530
10C520
㉠ SELECT 고객번호, SUM(청구액)
     FROM 청구
    WHERE 청구액 >= 100
    GROUP BY 고객번호;

㉡ SELECT 고객번호, SUM(청구액)
     FROM 청구
    GROUP BY 고객번호
   HAVING SUM(청구액) >= 100;
12, 3
25, 3
32, 2
43, 2
정답 및 해설

㉠은 WHERE 가 그룹화 전에 행 단위로 걸러 청구액 100 이상인 행이 있는 C1·C4만 남아 2행이다.

㉡은 모든 행을 묶은 뒤 HAVING 이 합계를 걸러 C1(150)·C2(110)·C4(200)의 3행이고, C3·C5는 합계 90이라 빠진다.

  • 3, 2는 WHERE 와 HAVING 의 결과를 서로 바꾼 것이다.
  • 2, 2는 C2 처럼 작은 청구를 합쳐 100 이상이 되는 그룹을 놓쳤다.
  • 5, 3은 WHERE 로 행이 모두 빠진 그룹도 남는다고 보았다.
문제 21
다음 SQL 중 실행 시 오류가 발생하는 것은?

[계약]

계약번호상품코드설계사
1A서윤
2A한결
3B서윤
4B서윤
5C태오
㉠ SELECT 상품코드, COUNT(*)
     FROM 계약
    GROUP BY 상품코드
   HAVING 상품코드 <> 'B';

㉡ SELECT COUNT(*)
     FROM 계약
    GROUP BY 상품코드, 설계사;

㉢ SELECT 상품코드, COUNT(*)
     FROM 계약
    GROUP BY 상품코드
   HAVING 설계사 = '서윤';

㉣ SELECT 상품코드, COUNT(DISTINCT 설계사)
     FROM 계약
    GROUP BY 상품코드;
1㉡
2㉢
3㉠
4㉣
정답 및 해설

HAVING 은 그룹마다 한 번 평가되므로 GROUP BY 열이나 집계 함수만 쓸 수 있는데, ㉢의 설계사는 그룹화되지 않은 열이라 오류가 난다.

설계사로 거르려면 그룹화 전에 처리되는 WHERE 절로 옮겨야 한다.

  • ㉠은 그룹화 열인 상품코드를 HAVING 에 써서 정상 실행된다.
  • ㉡은 GROUP BY 열을 SELECT 에 쓰지 않아도 되므로 4행이 나온다.
  • ㉣은 그룹별로 서로 다른 설계사 수를 세는 정상 집계다.
문제 22
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호등급보험료
1A50
2(null)30
3B70
4A90
5(null)60
6B40
SELECT 계약번호, 등급, 보험료
  FROM 계약
 ORDER BY 등급 DESC NULLS LAST, 보험료 DESC;
1
계약번호등급보험료
6B40
3B70
1A50
4A90
2(null)30
5(null)60
2
계약번호등급보험료
3B70
6B40
4A90
1A50
5(null)60
2(null)30
3
계약번호등급보험료
4A90
1A50
3B70
6B40
5(null)60
2(null)30
4
계약번호등급보험료
5(null)60
2(null)30
3B70
6B40
4A90
1A50
정답 및 해설

Oracle 은 NULL 을 가장 큰 값으로 보아 DESC 에서 맨 앞에 두지만 NULLS LAST 를 지정했으므로 B→A→NULL 순이 된다.

같은 등급 안에서는 두 번째 키 보험료 DESC 로 B(70, 40)·A(90, 50)·NULL(60, 30) 순으로 정렬된다.

  • NULL 이 맨 앞인 표는 NULLS LAST 를 무시한 기본 DESC 정렬이다.
  • 같은 등급 안에서 보험료가 오르는 표는 두 번째 키를 오름차순으로 보았다.
  • A 등급이 먼저인 표는 등급을 오름차순으로 정렬했다.
문제 23
계약 테이블은 6행(상태 '유지' 4행, '해지' 2행), 특약 테이블은 3행(그중 1행은 특약명이 NULL)이다. 다음 SQL 의 결과 행 수는?
SELECT K.계약번호, T.특약명
  FROM 계약 K CROSS JOIN 특약 T
 WHERE K.상태 = '유지';
112
28
318
46
정답 및 해설

CROSS JOIN 은 조인 조건 없이 모든 행을 짝짓는 카티션 곱이고, WHERE 가 계약 쪽만 '유지' 4행으로 거르므로 4 × 3 = 12행이다.

특약명이 NULL 인 행도 곱하기에서 빠지지 않으며, 조건이 없는 한 NULL 여부는 행 수에 영향을 주지 않는다.

  • 8행은 특약명이 NULL 인 행을 곱하기에서 뺀 결과다.
  • 18행은 WHERE 필터 전 6 × 3 전체 곱이다.
  • 6행은 계약 쪽 행 수만 남는다고 본 결과다.
문제 24
다음 SQL 의 실행 결과로 옳은 것은?

[고객]

고객번호고객명
1하늘
2바다
3구름

[계약]

계약번호고객번호상품명
101암보험
111실손
122운전자
13(null)단체상해
143치아

[청구]

청구번호계약번호청구액
10010300
1011150
1021180
10313120
SELECT C.고객명, K.상품명, Q.청구액
  FROM 고객 C, 계약 K, 청구 Q
 WHERE C.고객번호 = K.고객번호
   AND K.계약번호 = Q.계약번호
 ORDER BY Q.청구번호;
1
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
(null)단체상해120
2
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
바다운전자(null)
3
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
4
고객명상품명청구액
하늘암보험300
하늘실손50
정답 및 해설

세 테이블을 N-1 = 2개의 조인 조건으로 모두 이었고 INNER 조인이라 세 테이블 모두에 짝이 있는 청구 100·101·102 세 행만 남는다.

실손 계약에 청구가 둘이라 하늘·실손이 두 번 나오며, 고객번호가 NULL 인 단체상해와 청구 없는 운전자·치아는 빠진다.

  • 2행 표는 한 계약에 청구가 여러 건이면 한 행만 남는다고 보았다.
  • 단체상해 행이 있는 표는 NULL 고객번호도 조인된다고 보았다.
  • 운전자 행이 있는 표는 OUTER 조인처럼 짝 없는 계약을 남겼다.
문제 25
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호상품명
10암보험
11실손
12운전자
13치아

[청구]

청구번호계약번호상태
10010지급
10111반려
10211지급
10312반려
SELECT K.계약번호, K.상품명, Q.청구번호
  FROM 계약 K LEFT OUTER JOIN 청구 Q
    ON K.계약번호 = Q.계약번호
 WHERE Q.상태 <> '반려'
 ORDER BY K.계약번호, Q.청구번호;
1
계약번호상품명청구번호
10암보험100
11실손102
12운전자(null)
13치아(null)
2
계약번호상품명청구번호
10암보험100
11실손101
11실손102
13치아(null)
3
계약번호상품명청구번호
10암보험100
11실손102
4
계약번호상품명청구번호
10암보험100
11실손102
13치아(null)
정답 및 해설

조인 뒤 WHERE 가 적용되므로 반려 청구 101·103 행이 빠지고, 청구가 없어 상태가 NULL 인 치아 행도 NULL <> '반려' 가 UNKNOWN 이라 제거되어 2행만 남는다.

결국 바깥 조인의 보존 효과가 사라져 INNER 조인과 같아지며, 기준 행을 살리려면 이 조건을 ON 절로 옮겨야 한다.

  • 치아 행이 남은 표는 NULL 이 <> 조건을 통과한다고 보았다.
  • 운전자·치아가 NULL 로 남은 표는 조건을 ON 절에 둔 결과다.
  • 101 행이 남은 표는 WHERE 조건을 적용하지 않았다.
문제 26
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호상품코드채널보험료
1P1대면50
2P1온라인30
3P2(null)40
4P2대면45
5P3온라인20

[상품]

상품코드채널상품명
P1대면종신A
P1온라인종신B
P2대면실손A
P3TM암보험
SELECT *
  FROM 계약 NATURAL JOIN 상품
 ORDER BY 계약번호;
1
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2(null)340실손A
P2대면445실손A
2
계약번호상품코드채널보험료상품명
1P1대면50종신A
2P1온라인30종신B
4P2대면45실손A
3
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2대면445실손A
P3온라인520(null)
4
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2대면445실손A
정답 및 해설

NATURAL JOIN 은 이름이 같은 상품코드·채널 두 열이 모두 같은 행만 결합하고, SELECT * 에서 공통 열을 맨 앞에 한 번만 표시한다.

계약 3은 채널이 NULL 이라 등치 비교가 UNKNOWN 이 되어 빠지고, 계약 5는 (P3, 온라인) 짝이 상품에 없어 빠지므로 계약 1·2·4 세 행이 남는다.

  • 계약 3을 실손A 와 결합한 표는 NULL 인 공통 열을 비교에서 건너뛴다고 본 오해이다.
  • 계약번호를 맨 앞에 둔 표는 공통 열이 앞으로 모인다는 규칙을 놓쳤다.
  • 계약 5를 상품명 NULL 로 남긴 표는 외부 조인처럼 처리한 결과이다.
문제 27
Oracle 쿼리 ㉮와 같은 [결과]를 ANSI 쿼리 ㉯로 얻으려 한다. ( ㉠ )에 들어갈 구문으로 옳은 것은?

[계약]

계약번호고객명상태
101김하늘유지
102이도윤해지
103박서아유지
104최로운유지

[청구]

청구번호계약번호금액
1101120
2102300
310380
410350
510970

㉮

SELECT R.청구번호, C.고객명
  FROM 청구 R, 계약 C
 WHERE C.계약번호(+) = R.계약번호
   AND C.상태(+) = '유지'
 ORDER BY R.청구번호;

㉯

SELECT R.청구번호, C.고객명
  FROM 청구 R ( ㉠ ) 계약 C
    ON C.계약번호 = R.계약번호
   AND C.상태 = '유지'
 ORDER BY R.청구번호;

[결과]

청구번호고객명
1김하늘
2(null)
3박서아
4박서아
5(null)
1INNER JOIN
2LEFT OUTER JOIN
3RIGHT OUTER JOIN
4CROSS JOIN
정답 및 해설

(+) 가 붙은 계약 쪽은 짝이 없을 때 NULL 로 채워지는 쪽이므로, 보존할 청구를 왼쪽에 둔 LEFT OUTER JOIN 이 같은 결과를 낸다.

C.상태(+) = '유지' 처럼 (+) 가 붙은 조건은 ON 절 조건이 되어, 해지 계약의 청구 2와 계약이 없는 청구 5도 고객명 NULL 로 남는다.

  • RIGHT OUTER JOIN 은 계약을 보존해 102·104가 청구 NULL 로 나오고 청구 2·5는 빠진다.
  • INNER JOIN 은 짝이 맞는 청구 세 건만 남겨 NULL 행이 사라진다.
  • CROSS JOIN 은 조건 없이 모든 행을 곱하는 구문이라 ON 절을 붙이면 오류가 난다.
문제 28
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호계약번호청구금액
120150
220180
320260
420260
520390
6204(null)
720440
SELECT 청구번호, 계약번호, 청구금액
  FROM 청구 A
 WHERE 청구금액 = (SELECT MAX(청구금액)
                     FROM 청구 B
                    WHERE B.계약번호 = A.계약번호)
 ORDER BY 청구번호;
1
청구번호계약번호청구금액
220180
320260
520390
720440
2
청구번호계약번호청구금액
220180
320260
420260
520390
3
청구번호계약번호청구금액
220180
320260
420260
520390
720440
4
청구번호계약번호청구금액
220180
320260
420260
520390
6204(null)
720440
정답 및 해설

연관 서브쿼리는 바깥 행 A 의 계약번호를 받아 같은 계약 안의 MAX(청구금액)을 매번 다시 구하므로, 계약마다 최대 금액과 같은 청구가 모두 남는다.

202는 60이 두 건이라 두 건 모두 남고, 204는 MAX 가 NULL 을 무시해 40이 최대가 되며 금액이 NULL 인 청구 6은 = 비교가 UNKNOWN 이라 빠진다.

  • 202에서 한 건만 남긴 표는 동점을 하나로 줄인 것으로, = 비교는 같은 값을 모두 통과시킨다.
  • 204가 빠진 표는 NULL 이 섞이면 MAX 가 NULL 이 된다고 본 오해이다.
  • 청구 6을 넣은 표는 NULL 금액도 최댓값 비교를 통과한다고 본 것이다.
문제 29
계약별 청구 합계를 구해 다음 [결과]를 얻으려 한다. ( ㉠ )에 들어갈 식으로 옳은 것은?

[계약]

계약번호고객명
301한지우
302오세린
303문태오
304신가람

[청구]

청구번호계약번호금액
1301100
2301(null)
330270
4304(null)
SELECT C.계약번호, C.고객명,
       (SELECT ( ㉠ )
          FROM 청구 R
         WHERE R.계약번호 = C.계약번호) AS 청구합계
  FROM 계약 C
 ORDER BY C.계약번호;

[결과]

계약번호고객명청구합계
301한지우100
302오세린70
303문태오0
304신가람0
1SUM(R.금액)
2SUM(COALESCE(R.금액, 0))
3COUNT(R.금액)
4COALESCE(SUM(R.금액), 0)
정답 및 해설

집계 함수만 있는 스칼라 서브쿼리는 대상 행이 없어도 한 행을 돌려주므로, 그 SUM 결과를 COALESCE(SUM(R.금액), 0) 으로 감싸야 청구가 없는 303도 0이 된다.

301은 NULL 을 뺀 100, 302는 70이고, 금액이 NULL 뿐인 304와 청구가 없는 303은 SUM 이 NULL 이어서 0으로 바뀐다.

  • SUM(R.금액) 만 쓰면 303·304가 NULL 로 나온다.
  • SUM(COALESCE(R.금액, 0)) 은 304는 0이지만 대상 행이 없는 303은 여전히 NULL 이다.
  • COUNT(R.금액) 는 금액이 아닌 건수를 세어 301·302가 1이 된다.
문제 30
청구가 한 건도 없는 계약을 찾는 다음 SQL 이 0행을 반환했다. 청구.계약번호에 NULL 이 있기 때문일 때, 해결 방법으로 적절하지 않은 것은?
SELECT 계약번호
  FROM 계약
 WHERE 계약번호 NOT IN (SELECT 계약번호 FROM 청구);
1<> ALL 로 바꾼다
2NOT EXISTS 로 바꾼다
3서브쿼리에 IS NOT NULL 을 건다
4LEFT JOIN 후 IS NULL 로 거른다
정답 및 해설

<> ALL 은 NOT IN 과 같은 뜻이라 서브쿼리 결과에 NULL 이 있으면 비교가 UNKNOWN 이 되어 여전히 0행이다.

NOT IN (a, NULL) 은 x <> a AND x <> NULL 로 풀리는데, 뒤쪽이 항상 UNKNOWN 이라 전체가 참이 될 수 없다.

  • 서브쿼리에서 NULL 을 걸러 내면 비교 대상에 NULL 이 없어 정상 동작한다.
  • NOT EXISTS 는 행 존재 여부만 보므로 NULL 의 영향을 받지 않는다.
  • LEFT JOIN 후 청구 쪽 키가 NULL 인 행만 고르면 청구 없는 계약이 남는다.
문제 31
집합 연산자 UNION 과 UNION ALL 에 대한 설명으로 옳은 것은?
1UNION ALL 은 열 수가 달라도 된다
2UNION 은 NULL 끼리 다르게 본다
3합친 결과에 중복이 없으면 행 수가 같다
4UNION ALL 은 자동 정렬된다
정답 및 해설

UNION 은 합친 결과에서 중복 행을 제거하고 UNION ALL 은 그대로 두므로, 합친 결과에 중복 행이 없다면 두 연산의 행 수는 같다.

이 경우 중복 제거를 위한 정렬·비교 작업이 없는 UNION ALL 이 같은 결과를 더 적은 부하로 낸다.

  • UNION ALL 은 중복 제거도 정렬도 하지 않으며 순서는 ORDER BY 로만 정해진다.
  • UNION 은 중복을 판단할 때 NULL 끼리를 같은 값으로 본다.
  • 집합 연산은 모든 SELECT 의 열 개수와 데이터 형이 맞아야 한다.
문제 32
다음 SQL 의 실행 결과로 옳은 것은? (EXCEPT 는 Oracle 의 MINUS 와 같다)

[계약]

계약번호고객번호상품코드
1C1P1
2C1P1
3C1P2
4C2P1
5C3(null)
6C4P3

[청구]

청구번호고객번호상품코드
1C1P2
2C2P1
3C3(null)
4C5P3
SELECT 고객번호, 상품코드 FROM 계약
EXCEPT
SELECT 고객번호, 상품코드 FROM 청구
 ORDER BY 고객번호, 상품코드;
1
고객번호상품코드
C5P3
2
고객번호상품코드
C1P1
C4P3
3
고객번호상품코드
C1P1
C1P1
C4P3
4
고객번호상품코드
C1P1
C3(null)
C4P3
정답 및 해설

EXCEPT(MINUS) 는 위 결과에서 아래 결과에 있는 행을 빼고 중복을 제거하므로 (C1, P1) 과 (C4, P3) 두 행만 남는다.

(C1, P2)·(C2, P1)·(C3, NULL) 은 청구에도 있어 빠지는데, 집합 연산은 NULL 끼리를 같은 값으로 비교한다.

  • (C1, P1) 을 두 행 남긴 표는 EXCEPT ALL 처럼 중복을 유지한 결과이다.
  • (C3, NULL) 을 남긴 표는 NULL 끼리를 다른 값으로 본 오해이다.
  • (C5, P3) 만 있는 표는 두 SELECT 의 순서를 바꿔 청구에서 계약을 뺀 결과이다.
문제 33
다음 SQL 의 결과 행 수는?

[청구]

청구번호유형채널금액
1입원앱300
2입원창구500
3통원앱40
4통원앱60
5통원(null)30
6수술창구900
7입원앱200
SELECT 유형, 채널, SUM(금액) AS 합계
  FROM 청구
 GROUP BY CUBE (유형, 채널);
111
210
39
412
정답 및 해설

CUBE (유형, 채널) 는 (유형, 채널)·(유형)·(채널)·() 네 가지 그룹을 모두 만들므로 상세 5 + 유형 소계 3 + 채널 소계 3 + 총계 1 = 12행이다.

채널이 NULL 인 통원 청구는 상세 그룹 (통원, NULL) 과 채널 NULL 소계를 따로 만들며, 이 소계는 총계와 합쳐지지 않는다.

  • 9행은 ROLLUP 처럼 유형 소계와 총계만 센 값이다.
  • 11행은 채널 NULL 소계를 총계와 같은 행으로 본 값이다.
  • 10행은 채널이 NULL 인 데이터를 상세와 소계 모두에서 뺀 값이다.
문제 34
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호유형채널금액
1입원앱100
2입원(null)50
3통원앱30
4통원앱20
SELECT 유형, 채널, SUM(금액) AS 합계,
       GROUPING(채널) AS G
  FROM 청구
 GROUP BY GROUPING SETS ((유형, 채널), (유형))
 ORDER BY 유형, G, 채널;
1
유형채널합계G
입원앱1000
입원(null)500
입원(null)1001
통원앱500
통원(null)501
2
유형채널합계G
입원앱1000
입원(null)500
입원(null)1501
통원앱500
통원(null)501
(null)(null)2001
3
유형채널합계G
입원앱1000
입원(null)1500
입원(null)501
통원앱500
통원(null)501
4
유형채널합계G
입원앱1000
입원(null)500
입원(null)1501
통원앱500
통원(null)501
정답 및 해설

GROUPING SETS ((유형, 채널), (유형)) 은 유형·채널 상세와 유형 소계만 만들고 총계는 만들지 않으며, 소계 행의 GROUPING(채널) 만 1이다.

입원의 NULL 채널 50은 원래 데이터의 NULL 이라 G=0 인 상세 행이고, 입원 소계 150은 NULL 채널 금액까지 더한 값이다.

  • 원래 NULL 행에 1을 준 표는 GROUPING 이 집계로 생긴 NULL 에만 1을 준다는 점을 놓쳤다.
  • 총계 행이 있는 표는 () 를 지정하지 않았는데도 전체 합계를 넣었다.
  • 입원 소계를 100으로 쓴 표는 채널이 NULL 인 행을 소계에서 뺀 것이다.
문제 35
다음 [결과]를 얻기 위해 ( ㉠ )에 들어갈 구문으로 옳은 것은?

[계약]

계약번호지점상품군보험료
1강남종신30
2강남실손10
3분당종신20
4분당실손15
5분당실손5
SELECT 지점, 상품군, SUM(보험료) AS 합계
  FROM 계약
 GROUP BY ( ㉠ )
 ORDER BY 상품군, 지점;

[결과]

지점상품군합계
강남실손10
분당실손20
(null)실손30
강남종신30
분당종신20
(null)종신50
(null)(null)80
1ROLLUP (지점, 상품군)
2ROLLUP (상품군, 지점)
3지점, ROLLUP (상품군)
4CUBE (상품군, 지점)
정답 및 해설

[결과]에는 상품군별 소계(지점 NULL)와 총계만 있으므로, 상품군을 상위 계층으로 둔 ROLLUP (상품군, 지점) 이 맞다.

ROLLUP 은 인수 순서대로 (상품군, 지점), (상품군), () 단계로 집계하며 SELECT 절 열 순서와는 무관하다.

  • ROLLUP (지점, 상품군) 은 지점별 소계를 만들어 강남·분당 소계 행이 나온다.
  • CUBE 는 상품군 소계와 지점 소계를 모두 만들어 행이 더 많다.
  • 지점, ROLLUP (상품군) 은 지점별 소계만 만들고 총계 행이 없다.
문제 36
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객번호금액
1K1100
2K150
3K2200
4K3(null)
5K330
6K220
SELECT 고객번호, SUM(금액) AS 합계,
       COUNT(*) OVER () AS 고객수,
       SUM(SUM(금액)) OVER () AS 전체
  FROM 청구
 GROUP BY 고객번호
 ORDER BY 고객번호;
1
고객번호합계고객수전체
K11506400
K22206400
K3306400
2
고객번호합계고객수전체
K11503400
K22203400
K3303400
3
고객번호합계고객수전체
K11503150
K22203370
K3303400
4
고객번호합계고객수전체
K11503(null)
K22203(null)
K3(null)3(null)
정답 및 해설

윈도우 함수는 GROUP BY 가 끝난 뒤 그룹 결과 3행 위에서 계산되므로, 행 수는 고객 수인 3행이고 COUNT(*) OVER () 도 3이다.

SUM(SUM(금액)) OVER () 는 고객별 합계 150·220·30을 모두 더한 400을 각 행에 붙이며, K3 합계는 NULL 을 뺀 30이다.

  • 고객수를 6으로 쓴 표는 윈도우 함수가 원본 행에 적용된다고 본 오해이다.
  • K3 와 전체를 NULL 로 쓴 표는 집계 함수가 NULL 을 무시한다는 점을 놓쳤다.
  • 전체를 누적 값으로 쓴 표는 ORDER BY 가 없는 OVER () 를 누적 프레임으로 본 것이다.
문제 37
지점별 신계약 건수 순위로 다음 [결과]를 얻으려 한다. ( ㉠ )에 들어갈 함수로 옳은 것은?

[실적]

설계사지점건수
윤아강남9
도현강남9
시우강남8
하린강남7
지안판교6
서준판교5
예린판교5
SELECT 지점, 설계사, 건수
  FROM (SELECT 지점, 설계사, 건수,
               ( ㉠ ) OVER (PARTITION BY 지점
                            ORDER BY 건수 DESC) AS 순위
          FROM 실적) T
 WHERE 순위 <= 2
 ORDER BY 지점, 건수 DESC, 설계사;

[결과]

지점설계사건수
강남도현9
강남윤아9
강남시우8
판교지안6
판교서준5
판교예린5
1NTILE(2)
2RANK()
3ROW_NUMBER()
4DENSE_RANK()
정답 및 해설

[결과]에서 강남은 9건 두 명 뒤의 8건까지, 판교는 6건과 동점인 5건 두 명이 모두 나오므로 동점 뒤 순위를 건너뛰지 않는 DENSE_RANK() 가 맞다.

강남 순위는 DENSE_RANK 로 1·1·2·3, RANK 로 1·1·3·4 가 되어 동점 뒤 순위 처리에서 결과가 갈린다.

  • RANK() 는 강남 8건이 3위가 되어 강남에서 9건 두 명만 남는다.
  • ROW_NUMBER() 는 동점에도 고유 번호를 주어 판교 5건 중 한 명이 빠진다.
  • NTILE(2) 는 행을 두 묶음으로 나눌 뿐이라 모든 행이 2 이하가 되어 하린까지 나온다.
문제 38
다음 SQL 의 실행 결과로 옳은 것은?

[납입]

계약번호회차납입액
A011100
A012(null)
A013100
B02150
B02270
SELECT 계약번호, 회차, 납입액,
       SUM(납입액) OVER (PARTITION BY 계약번호
                         ORDER BY 회차 DESC) AS 잔여합
  FROM 납입
 ORDER BY 계약번호, 회차;
1
계약번호회차납입액잔여합
A011100100
A012(null)100
A013100200
B0215050
B02270120
2
계약번호회차납입액잔여합
A011100200
A012(null)200
A013100200
B02150120
B02270120
3
계약번호회차납입액잔여합
A011100(null)
A012(null)(null)
A013100100
B02150120
B0227070
4
계약번호회차납입액잔여합
A011100200
A012(null)100
A013100100
B02150120
B0227070
정답 및 해설

OVER 안의 ORDER BY 회차 DESC 때문에 누적은 계약별 마지막 회차부터 거꾸로 쌓이고, 출력 순서는 바깥 ORDER BY 에 따라 회차 오름차순이 된다.

A01은 3회차 100, 2회차는 NULL 을 건너뛴 누적이라 100, 1회차 200이며 B02는 2회차 70, 1회차 120이다.

  • 1회차부터 쌓은 표는 OVER 의 정렬 방향을 바깥 정렬과 같다고 본 것이다.
  • NULL 이후를 NULL 로 쓴 표는 SUM 이 NULL 을 건너뛴다는 점을 놓쳤다.
  • 계약별 총합을 모든 행에 쓴 표는 OVER 안의 ORDER BY 를 무시한 결과이다.
문제 39
다음 SQL 의 실행 결과로 옳은 것은?

[월별청구]

월금액
140
270
3(null)
420
560
SELECT 월, 금액,
       MAX(금액) OVER (ORDER BY 월
                       ROWS BETWEEN 1 PRECEDING
                                AND 1 FOLLOWING) AS 이웃최대,
       LEAD(금액, 1, 0) OVER (ORDER BY 월) AS 다음
  FROM 월별청구
 ORDER BY 월;
1
월금액이웃최대다음
1407070
270700
3(null)7020
4206060
560600
2
월금액이웃최대다음
1407070
27070(null)
3(null)7020
4206060
560600
3
월금액이웃최대다음
1407070
27070(null)
3(null)7020
4206060
56060(null)
4
월금액이웃최대다음
1404070
27070(null)
3(null)7020
4207060
560700
정답 및 해설

ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 은 앞·현재·뒤 한 행씩 세 행 중 최댓값을 구하므로 4월은 NULL·20·60 중 60이다.

LEAD(금액, 1, 0) 의 기본값 0은 다음 행이 없는 5월에만 쓰이고, 2월은 다음 행 금액이 실제 NULL 이라 NULL 이 나온다.

  • 2월 다음을 0으로 쓴 표는 기본값이 NULL 값까지 대신한다고 본 오해이다.
  • 이웃최대가 40·70·70·70·70 인 표는 처음부터 현재 행까지의 누적 프레임으로 계산했다.
  • 5월 다음을 NULL 로 쓴 표는 세 번째 인수인 기본값을 무시했다.
문제 40
Oracle(12c 이상)에서 보험료 상위 4~6위 계약을 조회하려 한다. 보험료에 NULL 과 동점이 없을 때, 실행은 되지만 결과가 항상 0행인 SQL 은?

㉠

SELECT 계약번호, 보험료
  FROM (SELECT ROWNUM AS RN, T.*
          FROM (SELECT * FROM 계약
                 ORDER BY 보험료 DESC) T
         WHERE ROWNUM <= 6)
 WHERE RN >= 4;

㉡

SELECT 계약번호, 보험료
  FROM (SELECT * FROM 계약
         ORDER BY 보험료 DESC)
 WHERE ROWNUM BETWEEN 4 AND 6;

㉢

SELECT 계약번호, 보험료
  FROM 계약
 ORDER BY 보험료 DESC
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;

㉣

SELECT 계약번호, 보험료
  FROM (SELECT 계약번호, 보험료,
               ROW_NUMBER() OVER (ORDER BY 보험료 DESC) AS RN
          FROM 계약)
 WHERE RN BETWEEN 4 AND 6;
1㉣
2㉡
3㉢
4㉠
정답 및 해설

ROWNUM 은 행을 꺼낼 때 1부터 차례로 붙으므로, 첫 행이 ROWNUM 4 이상 조건에서 탈락하면 다음 행도 다시 1을 받아 계속 탈락해 ROWNUM BETWEEN 4 AND 6 은 항상 0행이다.

4~6위를 얻으려면 ROWNUM 을 인라인 뷰 안에서 별칭(RN)으로 고정한 뒤 바깥에서 비교해야 한다.

  • 정렬한 인라인 뷰에서 ROWNUM 을 RN 으로 고정한 뒤 RN >= 4 로 거르면 4~6위가 나온다.
  • OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY 는 앞 3행을 건너뛰고 다음 3행을 가져온다.
  • ROW_NUMBER 로 매긴 RN 을 바깥에서 비교하면 4~6위가 정확히 나온다.
문제 41
SQL Server 에서 다음 SQL 의 결과 행 수는?

[계약]

계약번호고객명보험료
1가윤90
2나은80
3다인70
4라희70
5마루60
6바다50
7사랑40
SELECT TOP 30 PERCENT WITH TIES 계약번호, 보험료
  FROM 계약
 ORDER BY 보험료 DESC;
13
22
35
44
정답 및 해설

7행의 30%는 2.1행인데 TOP n PERCENT 는 소수 행을 올림해 3행을 고르고, WITH TIES 가 세 번째 행과 보험료가 같은 행을 더해 4행이 된다.

보험료 내림차순으로 90·80·70 다음에 같은 70인 계약이 동점으로 함께 나온다.

  • 2행은 2.1을 내림하고 동점도 무시한 값이다.
  • 3행은 올림은 했지만 WITH TIES 를 무시한 값이다.
  • 5행은 동점이 아닌 60까지 포함한 값이다.
문제 42
Oracle 에서 다음 SQL 을 실행했을 때, 코드가 D 인 행의 값으로 옳지 않은 것은?

[조직]

코드상위
A(null)
BA
CA
DB
SELECT LEVEL, 코드, PRIOR 코드 AS 부모,
       CONNECT_BY_ROOT 코드 AS 루트,
       CONNECT_BY_ISLEAF AS 말단
  FROM 조직
 START WITH 상위 IS NULL
CONNECT BY PRIOR 코드 = 상위;
1CONNECT_BY_ROOT 코드 = A
2PRIOR 코드 = A
3LEVEL 값 = 3
4CONNECT_BY_ISLEAF = 1
정답 및 해설

SELECT 절의 PRIOR 코드 는 현재 행의 부모 행 값을 돌려주므로, D 행에서는 부모인 B 가 나오고 A 가 아니다.

D 는 A→B→D 경로의 세 번째 단계라 LEVEL 이 3이고, 자식이 없어 CONNECT_BY_ISLEAF 는 1이다.

  • LEVEL 은 루트를 1로 시작해 한 단계 내려갈 때마다 1씩 늘어난다.
  • D 아래에는 자식 행이 없으므로 말단 표시가 1이다.
  • CONNECT_BY_ROOT 는 그 행이 속한 계층의 루트인 A 를 돌려준다.
문제 43
Oracle 에서 다음 SQL 의 실행 결과로 옳은 것은?

[추천]

고객추천인
K1K3
K2K1
K3K2
K4K2
SELECT LEVEL, 고객, CONNECT_BY_ISCYCLE AS 순환
  FROM 추천
 START WITH 고객 = 'K1'
CONNECT BY NOCYCLE PRIOR 고객 = 추천인
 ORDER SIBLINGS BY 고객;
1
LEVEL고객순환
1K10
2K20
3K31
3K40
2
LEVEL고객순환
1K10
2K20
3K31
3
LEVEL고객순환
1K10
2K20
3K31
4K10
3K40
4
LEVEL고객순환
1K11
2K20
3K30
3K40
정답 및 해설

NOCYCLE 은 K3 의 자식으로 이미 조상인 K1 이 다시 나오는 순환을 만나도 오류 없이 그 행을 내지 않고, 순환을 일으킨 K3 에 CONNECT_BY_ISCYCLE 1을 표시한다.

K1→K2 다음 K2 의 자식 K3·K4 가 형제로 ORDER SIBLINGS BY 고객 순서에 따라 나오며, 자식이 없는 K4 는 순환이 아니므로 0이다.

  • K4 가 빠진 표는 순환을 만나면 전체 전개가 멈춘다고 본 오해이다.
  • K1 에 1을 준 표는 다시 방문될 행에 순환 표시가 붙는다고 본 것이다.
  • LEVEL 4의 K1 을 넣은 표는 NOCYCLE 이 순환 행을 한 번 더 보여 준다고 본 것이다.
문제 44
Oracle 에서 다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객유형금액
1정민입원300
2정민통원40
3하늘통원60
4정민통원20
SELECT *
  FROM 청구
 PIVOT (SUM(금액) FOR 유형 IN ('입원' AS 입원, '통원' AS 통원))
 ORDER BY 청구번호;
1
청구번호고객유형입원통원
1정민입원300(null)
2정민통원(null)40
3하늘통원(null)60
4정민통원(null)20
2
청구번호고객입원통원
1정민300(null)
2정민(null)40
3하늘(null)60
4정민(null)20
3
고객입원통원
정민30060
하늘(null)60
4
청구번호고객입원통원
1정민3000
2정민040
3하늘060
4정민020
정답 및 해설

PIVOT 은 집계 대상(금액)과 FOR 열(유형)을 뺀 나머지 모든 열로 암묵적 GROUP BY 하므로, 청구번호가 남아 있는 이 쿼리는 원본 4행이 그대로 4행이 된다.

각 행은 자기 유형 칸에만 금액이 들어가고 다른 칸은 NULL 이며, 고객별로 합치려면 인라인 뷰에서 청구번호를 빼고 PIVOT 해야 한다.

  • 고객별 2행으로 합친 표는 청구번호가 그룹 기준이 된다는 점을 놓쳤다.
  • 빈 칸을 0으로 쓴 표는 해당 값이 없을 때 SUM 이 NULL 을 돌려준다는 점을 놓쳤다.
  • 유형 열이 남은 표는 FOR 절의 열이 결과 열로 바뀌며 사라진다는 점을 놓쳤다.
문제 45
Oracle 에서 다음 SQL 의 결과 값은?

[계약]

계약번호증권번호
1LF-2026
2NL2027
3lf-2026
4LFNL-2026
5NL-20261
6NL-0007
7LF-202
SELECT COUNT(*)
  FROM 계약
 WHERE REGEXP_LIKE(증권번호, '^(LF|NL)-?\d{4}$');
14
23
31
42
정답 및 해설

패턴은 LF 또는 NL 로 시작해 하이픈이 0~1개(-?) 온 뒤 숫자 정확히 4개로 끝나야 하므로 LF-2026, NL2027, NL-0007 세 행이 일치한다.

REGEXP_LIKE 는 기본으로 대소문자를 구분하고, ^ 와 $ 앵커가 앞뒤에 다른 문자가 붙는 것을 막는다.

  • lf-2026 은 소문자라 LF 와 일치하지 않는다.
  • LFNL-2026 은 선택 그룹이 한 번만 와야 하는데 LF 뒤에 NL 이 붙어 탈락한다.
  • NL-20261 과 LF-202 는 숫자가 4개가 아니어서 끝 조건을 만족하지 못한다.
문제 46
[해지계약] 테이블이 [계약]과 같은 구조의 빈 테이블일 때, 다음 문장을 실행한 뒤 (계약 행 수, 해지계약 행 수)로 옳은 것은?

[계약]

계약번호상태보험료
1유지30
2해지20
3유지45
4(null)15
5해지25
6유지10
INSERT INTO 해지계약
SELECT * FROM 계약 WHERE 상태 = '해지';
DELETE FROM 계약 WHERE 상태 = '해지';
COMMIT;
14, 2
26, 2
33, 2
44, 0
정답 및 해설

INSERT … SELECT 는 조건에 맞는 해지 2행을 해지계약에 복사하고, 이어진 DELETE 가 계약에서 같은 2행을 지워 (4, 2) 가 된다.

상태가 NULL 인 계약은 = '해지' 비교가 UNKNOWN 이라 복사도 삭제도 되지 않고 계약에 남는다.

  • (3, 2) 는 상태가 NULL 인 행까지 삭제된다고 본 값이다.
  • (4, 0) 은 INSERT … SELECT 가 행을 넣지 못한다고 본 값이다.
  • (6, 2) 는 DELETE 의 결과를 반영하지 않은 값이다.
문제 47
ANSI 표준 격리 수준 기준으로, 다음 T1 에서 일어난 현상을 막을 수 있는 가장 낮은 격리 수준은?
T1: SELECT 금액 FROM 청구 WHERE 청구번호 = 7;   -- 300
T2: UPDATE 청구 SET 금액 = 450 WHERE 청구번호 = 7;
T2: COMMIT;
T1: SELECT 금액 FROM 청구 WHERE 청구번호 = 7;   -- 450
1READ COMMITTED
2SERIALIZABLE
3어느 수준으로도 막을 수 없다
4REPEATABLE READ
정답 및 해설

T1 이 이미 읽은 행을 T2 가 수정·커밋해 같은 조회의 값이 달라졌으므로 Non-Repeatable Read 이고, 이를 막는 가장 낮은 수준은 REPEATABLE READ 이다.

REPEATABLE READ 는 트랜잭션이 읽은 행이 끝날 때까지 바뀌지 않게 보장하며, 새 행이 나타나는 팬텀까지 막으려면 SERIALIZABLE 이 필요하다.

  • 격리 수준을 REPEATABLE READ 이상으로 올리면 이 현상은 막을 수 있다.
  • READ COMMITTED 는 Dirty Read 만 막고 커밋된 수정은 다시 읽을 때 보인다.
  • SERIALIZABLE 로도 막히지만 가장 낮은 수준은 아니다.
문제 48
Oracle 에서 다음 [청구] 테이블에 실행했을 때 오류가 발생하는 문장은?
CREATE TABLE 청구 (
  청구번호 NUMBER(5),
  금액     NUMBER(9),
  사유     VARCHAR2(30)
);
-- 3행 입력·커밋, 모든 행의 금액·사유에 값이 있음
1ALTER TABLE 청구 MODIFY (금액 VARCHAR2(9))
2ALTER TABLE 청구 RENAME TO 청구이력
3ALTER TABLE 청구 DROP COLUMN 사유
4ALTER TABLE 청구 ADD (메모 VARCHAR2(100))
정답 및 해설

값이 들어 있는 열은 데이터 형을 바꿀 수 없으므로, 숫자 열인 금액을 VARCHAR2 로 MODIFY 하면 열이 비어 있어야 한다는 오류가 난다.

형을 바꾸려면 열 값을 모두 NULL 로 비우거나, 새 열을 ADD 해 값을 옮긴 뒤 기존 열을 지워야 한다.

  • 새 열 추가는 기존 행에 NULL 이 채워지므로 데이터가 있어도 된다.
  • RENAME TO 는 테이블 이름을 바꾸는 구문으로 정상 실행된다.
  • DROP COLUMN 은 데이터가 있어도 열과 값을 함께 지운다.
문제 49
다음 정의와 데이터에서 DELETE 문을 실행한 뒤 (계약 행 수, 청구 행 수)로 옳은 것은?

[계약]

계약번호고객명
501윤채원
502장태민
503홍유나

[청구]

청구번호계약번호금액
1501100
250240
350160
450390
550230
CREATE TABLE 청구 (
  청구번호 INT PRIMARY KEY,
  계약번호 INT REFERENCES 계약(계약번호)
           ON DELETE CASCADE,
  금액     INT
);

DELETE FROM 계약 WHERE 계약번호 = 501;
12, 4
23, 5
32, 3
42, 5
정답 및 해설

ON DELETE CASCADE 는 부모 행이 삭제될 때 그 행을 참조하는 자식 행도 함께 삭제하므로, 계약 501과 그 청구 2건이 지워져 (2, 3) 이 된다.

다른 계약을 참조하는 청구 3건은 영향을 받지 않고 그대로 남는다.

  • (2, 5) 는 자식 행을 남기고 FK 만 NULL 로 바꾸는 SET NULL 의 동작이다.
  • (3, 5) 는 자식이 있으면 부모 삭제를 막는 RESTRICT 의 동작이다.
  • (2, 4) 는 연쇄 삭제가 자식 한 건에만 적용된다고 본 값이다.
문제 50
Oracle 에서 다음 두 문장을 실행한 뒤의 설명으로 옳은 것은?
GRANT SELECT ON 청구 TO KIM WITH GRANT OPTION;
GRANT CREATE VIEW TO LEE WITH ADMIN OPTION;
1ADMIN OPTION 은 객체 권한용
2KIM 은 INSERT 도 할 수 있다
3KIM 은 SELECT 를 넘길 수 있다
4LEE 는 CREATE VIEW 를 못 준다
정답 및 해설

WITH GRANT OPTION 으로 객체 권한을 받은 KIM 은 청구의 SELECT 권한을 다른 사용자에게 다시 부여할 수 있다.

시스템 권한에는 WITH ADMIN OPTION 을 쓰며, 이를 받은 LEE 도 CREATE VIEW 를 다른 사용자에게 줄 수 있다.

  • LEE 는 ADMIN OPTION 을 받았으므로 CREATE VIEW 를 넘길 수 있다.
  • KIM 이 받은 권한은 SELECT 뿐이라 INSERT 는 할 수 없다.
  • ADMIN OPTION 은 시스템 권한용이고 객체 권한에는 GRANT OPTION 을 쓴다.
SQLD SQL 개발자 실전 모의고사모두CBT · moducbt.com
SQL 개발자 기출유형 모의고사 10회
총 50문항 · 객관식 · 정답·해설 포함
문제 1
보험 계약 시스템을 운영하던 중 [계약] 엔터티의 구조를 바꾸자, 이를 쓰는 수십 개의 프로그램과 화면을 함께 고쳐야 했다. 데이터 모델링이 중요한 이유 중 이 상황과 가장 관련 깊은 것은?
1데이터 독립성(Independence)
2간결한 표현(Conciseness)
3파급효과(Leverage)
4데이터 품질(Data Quality)
정답 및 해설

데이터 모델은 많은 프로그램이 공통으로 기대는 기반이라 설계 후반이나 운영 중에 바꾸면 영향이 넓게 퍼지는데, 이를 파급효과(Leverage)라 한다.

그래서 모델은 개발 초기에 충분히 검토해 확정해야 하며, 늦게 바꿀수록 수정 비용이 커진다.

  • 간결한 표현은 모델이 업무 요구를 정확하고 간결하게 전달하는 역할을 말한다.
  • 데이터 품질은 중복·비일관 데이터로 생기는 정확성 문제를 가리킨다.
  • 데이터 독립성은 3단계 스키마 구조의 성질로, 모델링이 중요한 이유(파급효과·간결한 표현·데이터 품질)에 들지 않는다.
문제 2
보험상품처럼 물리적인 형태는 없지만 관리할 가치가 있는 개념적 정보를 나타내는 엔터티를 유무형에 따라 분류하면 무엇인가?
1사건 엔터티
2개념 엔터티
3행위 엔터티
4유형 엔터티
정답 및 해설

유무형에 따른 분류에서 형태는 없지만 관리해야 할 개념적 정보는 개념 엔터티로, 보험상품·조직 등이 해당한다.

같은 기준의 다른 분류로 물리적 형태가 있는 유형 엔터티와 업무 수행 중 발생하는 사건 엔터티가 있다.

  • 유형 엔터티는 고객·설계사처럼 물리적 형태가 있는 대상이다.
  • 사건 엔터티는 청구 접수·납입처럼 업무 중 발생하는 대상이다.
  • 행위 엔터티는 발생 시점에 따른 분류라 기준이 다르다.
문제 3
계약 엔터티의 속성 중 파생 속성(Derived Attribute)에 해당하는 것은?
1납입 내역을 합산한 누적납입액
2청약서에 적힌 계약 시작일
3업무 편의로 부여한 상태코드
4가입 시 정한 월 보험료
정답 및 해설

다른 속성 값을 계산·집계해서 얻는 속성이 파생 속성이며, 납입 내역을 합한 누적납입액이 이에 해당한다.

파생 속성은 원천 값이 바뀔 때마다 함께 갱신해야 하므로 데이터 정합성 관리를 위해 꼭 필요한 경우에만 둔다.

  • 청약서에 적힌 계약 시작일은 업무에서 나온 기본 속성이다.
  • 업무 편의로 부여한 상태코드는 설계 속성이다.
  • 가입 시 정한 월 보험료는 업무에서 나온 기본 속성이다.
문제 4
다음 ERD의 계약특약 엔터티에 대한 설명으로 옳은 것은?

그림

1계약과 특약의 M:N 관계를 해소한다
2주식별자는 특약코드 하나뿐이다
3계약 없이도 인스턴스가 생성된다
4특약의 상위 유형을 나타낸 엔터티다
정답 및 해설

계약 하나에 여러 특약이, 특약 하나가 여러 계약에 붙는 M:N 관계를 계약특약이라는 교차 엔터티로 1:N 두 개로 풀어낸 모델이다.

두 관계가 모두 식별 관계라 계약번호와 특약코드를 함께 상속받아 복합 주식별자를 이룬다.

  • 상위 유형(슈퍼타입)이 아니라 두 엔터티 사이의 교차 엔터티다.
  • 식별 관계로 계약번호를 상속하므로 계약 없이 생성될 수 없다.
  • 주식별자는 계약번호와 특약코드의 조합이다.
문제 5
다음 ERD에서 청구의 주식별자를 사고접수번호로 정하였다. 사고접수번호는 자동차 사고 청구에만 외부 기관이 발급하고, 질병 청구는 이 값 없이 접수된다. 이 설계가 어기는 주식별자 특성은?

그림

1존재성
2유일성
3불변성
4최소성
정답 및 해설

주식별자는 모든 인스턴스에 반드시 값이 있어야 하는 존재성을 갖춰야 하는데, 질병 청구는 사고접수번호가 비어 있으므로 이를 어긴다.

이런 경우 모든 청구에 값이 생기는 청구번호 같은 속성을 주식별자로 두고 사고접수번호는 일반 속성으로 내린다.

  • 발급된 사고접수번호끼리는 겹치지 않아 유일성 문제는 아니다.
  • 속성 하나로 이루어져 최소성은 충족한다.
  • 한 번 발급된 번호가 바뀌는 상황은 제시되지 않았다.
문제 6
다음 릴레이션에 대한 설명으로 옳은 것은?
상담배정(고객번호, 상담분야, 상담사)
후보키: (고객번호, 상담분야), (고객번호, 상담사)
함수 종속:
  (고객번호, 상담분야) → 상담사
  상담사 → 상담분야   -- 상담사는 한 분야만 맡는다
1부분 종속이 있어 2NF 위반이다
2상담사의 이행 종속으로 3NF 위반이다
33NF는 만족하나 BCNF는 위반이다
4BCNF 분해 시 모든 종속이 보존된다
정답 및 해설

결정자 상담사가 후보키가 아니므로 BCNF 위반이지만, 종속되는 상담분야가 후보키의 일부(주요 속성)라 3NF는 만족한다.

모든 속성이 어느 후보키에 속해 있어 키가 아닌 속성의 부분·이행 종속이 생기지 않는다.

  • 부분 종속은 키가 아닌 속성에 대한 것이라 2NF 위반이 아니다.
  • 상담분야는 주요 속성이라 이행 종속에 의한 3NF 위반이 아니다.
  • (상담사, 상담분야)로 분해하면 (고객번호, 상담분야) → 상담사 종속이 보존되지 않는다.
문제 7
고객과 계약처럼 1:N 관계로 연결된 두 테이블을 조인하는 원리에 대한 설명으로 옳은 것은?
1조인 조건은 기본키끼리만 쓸 수 있다
21:N 관계의 외래키는 1 쪽에 둔다
3자식 외래키와 부모 기본키 값을 비교한다
4두 테이블의 열 이름이 같아야 한다
정답 및 해설

모델의 관계는 자식 테이블의 외래키(FK) 값과 부모 테이블의 기본키(PK) 값이 같은 행을 잇는 조인으로 구현된다.

고객-계약이라면 계약.고객번호 = 고객.고객번호 조건으로 각 계약에 그 계약자 정보를 붙인다.

  • 열 이름이 달라도 값의 의미가 같으면 조인할 수 있다.
  • 1:N 관계의 외래키는 N 쪽(자식) 테이블에 둔다.
  • 조인 조건은 PK 가 아닌 일반 열끼리도 쓸 수 있다.
문제 8
보험 업무 모델에서 하나의 트랜잭션으로 묶어 원자성을 보장해야 하는 작업으로 가장 적절한 것은? (지급이 생기면 해당 청구는 반드시 지급완료 상태가 되어야 하며, 고객은 계약 없이도 등록할 수 있다)
1특약 상품 등록과 설계사 정보 수정
2신규 고객 등록과 이후의 첫 계약
3청구 내역 조회와 화면 출력 기록
4보험금 지급 입력과 청구 상태 변경
정답 및 해설

지급 내역 입력과 청구 상태 변경은 한쪽만 반영되면 데이터가 어긋나므로 하나의 트랜잭션으로 묶어 원자성을 보장해야 한다.

모델에서는 이처럼 함께 생겨야 하는 엔터티·속성 변경을 필수 관계로 표현해 같은 작업 단위임을 드러낸다.

  • 고객은 계약 없이 등록되는 선택 관계라 따로 처리해도 된다.
  • 특약 등록과 설계사 수정은 서로 관련 없는 별개 작업이다.
  • 조회와 출력 기록은 함께 확정해야 할 데이터 변경이 아니다.
문제 9
NULL 에 대한 설명으로 옳은 것은?
1NULL에 0을 더하면 0이다
2COUNT(열)은 NULL도 센다
3NULL끼리 = 비교는 UNKNOWN이다
4MAX는 NULL을 최댓값으로 본다
정답 및 해설

NULL 은 알 수 없는 값이라 NULL 끼리 = 로 비교해도 TRUE 가 아니라 UNKNOWN이 되며, 그래서 IS NULL 로 검사해야 한다.

그룹 함수는 NULL 을 제외하고 계산하고, 산술 연산에 NULL 이 섞이면 결과도 NULL 이다.

  • COUNT(열)은 NULL 을 세지 않고 COUNT(*)만 모든 행을 센다.
  • NULL 에 어떤 수를 더해도 결과는 NULL 이다.
  • MAX 는 NULL 을 무시하고 나머지 값 중 최댓값을 구한다.
문제 10
다음 청구 테이블은 청구ID(일련번호)를 기본키로 쓴다. 업무 규칙상 같은 계약·같은 사고일자·같은 청구차수의 청구는 한 건만 있어야 할 때, 이 테이블에 대한 설명으로 옳은 것은?

[청구]

청구ID계약번호사고일자청구차수청구금액
1K012026-03-021120
2K022026-03-02180
3K012026-03-02260
4K032026-03-101200
5K012026-04-15190
6K032026-03-101200
1업무상 같은 청구가 중복 입력돼 있다
2사고일자·차수 UNIQUE로 충분하다
3PK가 있어 중복 청구는 생길 수 없다
4계약번호 UNIQUE면 중복만 막는다
정답 및 해설

청구ID 4와 6은 계약 K03·사고일자 03-10·차수 1이 같은 업무상 중복인데, 인조식별자인 청구ID 값만 달라 기본키로는 걸러지지 않았다.

막으려면 (계약번호, 사고일자, 청구차수) 세 속성 조합 전체에 UNIQUE 제약을 걸어야 한다.

  • 인조식별자 PK 는 값만 다르면 같은 업무 데이터도 받아들인다.
  • 계약번호만 UNIQUE면 K01 의 정상적인 여러 청구까지 막힌다.
  • 사고일자·차수만 UNIQUE면 K01 과 K02 의 정상 청구가 충돌한다.
문제 11
[계약] 테이블의 납입주기 열에는 '월납', '분기납', '연납' 중 하나만 들어갈 수 있다. 이처럼 한 속성이 가질 수 있는 값들의 집합을 관계형 모델에서 무엇이라 하는가?
1인스턴스(Instance)
2튜플(Tuple)
3도메인(Domain)
4스키마(Schema)
정답 및 해설

관계형 모델에서 한 속성이 취할 수 있는 원자값들의 집합을 도메인(Domain)이라 하므로 납입주기의 허용값 집합은 도메인이다.

실제 테이블에서는 데이터 타입과 CHECK 제약 등으로 도메인을 구현해 범위 밖 값이 들어오지 못하게 한다.

  • 튜플은 테이블의 한 행, 즉 계약 한 건의 값 묶음이다.
  • 스키마는 테이블 이름과 속성 구성 같은 구조 정의이다.
  • 인스턴스는 어느 시점에 테이블에 실제로 들어 있는 행들의 집합이다.
문제 12
SELECT 절의 열 별칭(Alias)에 대한 설명으로 옳은 것은?
1별칭은 작은따옴표로 감싸야 한다
2별칭 앞에는 반드시 AS를 써야 한다
3별칭은 WHERE 절에서 쓸 수 있다
4공백이 든 별칭은 큰따옴표로 감싼다
정답 및 해설

별칭에 공백·특수문자가 있거나 대소문자를 그대로 두려면 큰따옴표로 감싸야 한다. 예: 월보험료 * 12 AS "연간 보험료"

별칭 앞의 AS 는 생략할 수 있고, 별칭은 SELECT 이후에 처리되는 ORDER BY 에서 쓸 수 있다.

  • AS 는 선택 사항이라 생략해도 된다.
  • WHERE 는 SELECT 보다 먼저 처리되어 별칭을 쓸 수 없다.
  • 작은따옴표는 문자열 상수를 나타낼 때 쓴다.
문제 13
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호설계사월보험료
1한결60
2한결40
3한결100
4서윤70
5서윤80
6지호30
7지호150
8태오50
9태오120
10서윤45
SELECT 설계사, SUM(월보험료) AS 합계, COUNT(*) AS 건수
  FROM 계약
 WHERE 월보험료 >= 50
 GROUP BY 설계사
HAVING COUNT(*) >= 2
 ORDER BY 합계 DESC;
1
설계사합계건수
서윤1502
한결1602
태오1702
2
설계사합계건수
태오1702
한결1603
서윤1503
3
설계사합계건수
태오1702
한결1602
서윤1502
4
설계사합계건수
태오1702
한결1602
서윤1502
지호1501
정답 및 해설

WHERE 가 그룹화보다 먼저 실행되어 50 미만인 계약 2·6·10번이 빠진 뒤 집계되므로 태오 170·한결 160·서윤 150(각 2건)이 남는다.

지호는 남은 행이 1건이라 HAVING 에서 제외되고, ORDER BY 는 SELECT 다음에 처리되어 별칭 합계로 내림차순 정렬된다.

  • 지호 행이 남은 표는 HAVING 조건을 적용하지 않은 결과다.
  • 건수가 3인 표는 WHERE 이전 행으로 COUNT 를 센 결과다.
  • 서윤부터 나온 표는 정렬 방향이 오름차순으로 뒤집혔다.
문제 14
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호청구액지급액
101500450
1023000
103200(null)
104(null)0
105150150
SELECT 청구번호,
       COALESCE(NULLIF(지급액, 0), 청구액) AS 기준액,
       CASE WHEN 지급액 > 0 THEN '지급'
            WHEN 지급액 = 0 THEN '부지급'
            ELSE '심사중' END AS 상태
  FROM 청구
 ORDER BY 청구번호;
1
청구번호기준액상태
101450지급
102300부지급
103200심사중
104(null)부지급
105150지급
2
청구번호기준액상태
101450지급
102300부지급
103200부지급
104(null)부지급
105150지급
3
청구번호기준액상태
101450지급
102300부지급
103200심사중
1040부지급
105150지급
4
청구번호기준액상태
101450지급
1020부지급
103200심사중
104(null)부지급
105150지급
정답 및 해설

NULLIF(지급액, 0)은 0을 NULL 로 바꾸므로 102·104는 청구액으로 넘어가 102는 300, 청구액도 NULL 인 104는 COALESCE 결과가 NULL 이다.

103의 지급액 NULL 은 두 WHEN 비교가 모두 UNKNOWN 이라 ELSE 의 심사중이 되고, 104는 지급액이 0이라 부지급이다.

  • 102 기준액이 0인 표는 NULLIF 변환을 빠뜨렸다.
  • 103이 부지급인 표는 NULL 을 0처럼 비교했다.
  • 104 기준액이 0인 표는 모든 인수가 NULL 인 COALESCE 를 0으로 보았다.
문제 15
Oracle 기준으로 다음 SQL 의 실행 결과(건수)는? (상품코드의 데이터 타입은 VARCHAR2 이다)

[계약]

계약번호상품코드
110
2010
3100
40010
51
620
SELECT COUNT(*) AS 건수
  FROM 계약
 WHERE 상품코드 = 10;
13
22
31
44
정답 및 해설

Oracle 은 문자와 숫자를 비교하면 문자 쪽을 숫자로 바꾸는 암시적 형변환을 하므로 조건은 TO_NUMBER(상품코드) = 10 으로 처리된다.

그래서 앞자리 0이 사라진 '10'·'010'·'0010' 세 행이 모두 10과 같아 건수는 3이다.

  • 1건은 문자열 '10' 과 정확히 같은 값만 센 결과다.
  • 2건은 '0010' 을 빠뜨린 결과다.
  • 4건은 '100' 이나 '1' 까지 같다고 본 결과다.
문제 16
다음 결과를 얻기 위해 ( 가 )에 들어갈 식으로 옳은 것은?

[고객청구]

고객번호금액
11500
2300
3(null)
4120
51000
6299
SELECT 고객번호, 금액,
       ( 가 ) AS 등급
  FROM 고객청구
 ORDER BY 고객번호;

㉠ CASE WHEN 금액 >= 300 THEN 'M'
        WHEN 금액 >= 1000 THEN 'H' ELSE 'L' END
㉡ CASE WHEN 금액 >= 1000 THEN 'H'
        WHEN 금액 >= 300 THEN 'M' END
㉢ CASE WHEN 금액 >= 1000 THEN 'H'
        WHEN 금액 >= 300 THEN 'M' ELSE 'L' END
㉣ CASE WHEN 금액 < 300 THEN 'L'
        WHEN 금액 < 1000 THEN 'M' ELSE 'H' END

[원하는 결과]

고객번호금액등급
11500H
2300M
3(null)L
4120L
51000H
6299L
1㉢
2㉡
3㉠
4㉣
정답 및 해설

검색 CASE 는 위에서부터 처음 참이 되는 WHEN 하나만 적용하므로 큰 기준(1000)을 먼저 두고, 나머지는 ELSE 로 L 을 주어야 한다.

금액이 NULL 인 고객 3은 모든 비교가 UNKNOWN 이라 ELSE 의 L 이 되고, 299인 고객 6은 300 미만이라 L 이다.

  • ㉠은 1500도 첫 조건 300 이상에 걸려 H 가 나오지 않는다.
  • ㉡은 ELSE 가 없어 고객 3·4·6의 등급이 NULL 이 된다.
  • ㉣은 NULL 인 고객 3이 ELSE 로 가서 H 가 된다.
문제 17
다음 WHERE 절과 같은 의미의 조건은?
WHERE 상태 = '유지' OR 상태 = '휴면' AND 보험료 >= 10
1(상태='유지' AND 보험료>=10) OR 상태='휴면'
2상태='휴면' AND (상태='유지' OR 보험료>=10)
3상태 IN ('유지','휴면') AND 보험료>=10
4상태='유지' OR (상태='휴면' AND 보험료>=10)
정답 및 해설

논리 연산자는 NOT → AND → OR 순으로 처리되므로 괄호가 없으면 AND 로 이어진 두 조건이 먼저 묶인다.

따라서 유지 상태 계약은 보험료와 관계없이 모두, 휴면 계약은 보험료 10 이상만 남는 A OR (B AND C) 형태가 된다.

  • OR 를 먼저 묶으면 유지 계약에도 보험료 조건이 붙는다.
  • 보험료 조건을 유지 쪽에 붙이면 AND 의 결합 대상을 잘못 본 것이다.
  • 휴면을 바깥으로 빼면 유지 계약이 전부 빠진다.
문제 18
다음 SQL 의 실행 결과로 옳은 것은?

[상품]

상품번호상품코드상품명
1CI_01암진단
2CI02암수술
3CI_뇌혈관
4CIX01입원일당
5XCI_1골절
6CI_A통원
SELECT 상품번호, 상품코드
  FROM 상품
 WHERE 상품코드 LIKE 'CI#_%' ESCAPE '#'
 ORDER BY 상품번호;
1
상품번호상품코드
1CI_01
6CI_A
2
상품번호상품코드
1CI_01
3CI_
6CI_A
3
상품번호상품코드
1CI_01
2CI02
3CI_
4CIX01
6CI_A
4
상품번호상품코드
1CI_01
3CI_
5XCI_1
6CI_A
정답 및 해설

ESCAPE '#' 로 #_ 는 밑줄 문자 그 자체가 되어 패턴은 'CI_' 로 시작하는 값이고, 뒤의 % 는 0글자 이상과 일치하므로 상품번호 1·3·6이 남는다.

CI02·CIX01은 세 번째 글자가 밑줄이 아니라 빠지고, XCI_1은 앞부분이 CI 가 아니라 빠진다.

  • CI02·CIX01이 포함된 표는 #_ 를 한 글자 와일드카드로 본 결과다.
  • CI_ 가 빠진 표는 % 가 한 글자 이상을 요구한다고 본 결과다.
  • XCI_1이 포함된 표는 패턴이 중간에서도 맞는다고 본 결과다.
문제 19
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객번호청구일지급액
1112026-03-28100
2122026-04-02200
3112026-04-05(null)
4132026-04-10150
5122026-04-18(null)
6142026-03-3080
7112026-04-2250
8(null)2026-04-2570
SELECT COUNT(*) AS 전체, COUNT(지급액) AS 지급건,
       COUNT(DISTINCT 고객번호) AS 고객수,
       SUM(지급액) AS 합계
  FROM 청구
 WHERE 청구일 >= DATE '2026-04-01';
1
전체지급건고객수합계
643(null)
2
전체지급건고객수합계
663470
3
전체지급건고객수합계
644470
4
전체지급건고객수합계
643470
정답 및 해설

GROUP BY 가 없으므로 WHERE 로 남은 청구 2·3·4·5·7·8번 6행 전체가 한 그룹이 되어 결과는 1행이다.

지급액 NULL 2건을 뺀 지급건 4, NULL 고객번호를 뺀 고객 11·12·13의 3명, 합계 200+150+50+70=470으로 NULL 은 제외된다.

  • 지급건 6인 표는 COUNT(열)이 NULL 도 센다고 보았다.
  • 고객수 4인 표는 DISTINCT 집계에 NULL 을 포함했다.
  • 합계가 NULL 인 표는 SUM 에 NULL 이 섞이면 NULL 이 된다고 보았다.
문제 20
다음 두 SQL ㉠, ㉡의 결과 행 수를 차례로 나타낸 것은?

[청구]

청구번호고객번호청구액
1C1120
2C130
3C260
4C250
5C390
6C4200
7C4(null)
8C540
9C530
10C520
㉠ SELECT 고객번호, SUM(청구액)
     FROM 청구
    WHERE 청구액 >= 100
    GROUP BY 고객번호;

㉡ SELECT 고객번호, SUM(청구액)
     FROM 청구
    GROUP BY 고객번호
   HAVING SUM(청구액) >= 100;
12, 3
25, 3
32, 2
43, 2
정답 및 해설

㉠은 WHERE 가 그룹화 전에 행 단위로 걸러 청구액 100 이상인 행이 있는 C1·C4만 남아 2행이다.

㉡은 모든 행을 묶은 뒤 HAVING 이 합계를 걸러 C1(150)·C2(110)·C4(200)의 3행이고, C3·C5는 합계 90이라 빠진다.

  • 3, 2는 WHERE 와 HAVING 의 결과를 서로 바꾼 것이다.
  • 2, 2는 C2 처럼 작은 청구를 합쳐 100 이상이 되는 그룹을 놓쳤다.
  • 5, 3은 WHERE 로 행이 모두 빠진 그룹도 남는다고 보았다.
문제 21
다음 SQL 중 실행 시 오류가 발생하는 것은?

[계약]

계약번호상품코드설계사
1A서윤
2A한결
3B서윤
4B서윤
5C태오
㉠ SELECT 상품코드, COUNT(*)
     FROM 계약
    GROUP BY 상품코드
   HAVING 상품코드 <> 'B';

㉡ SELECT COUNT(*)
     FROM 계약
    GROUP BY 상품코드, 설계사;

㉢ SELECT 상품코드, COUNT(*)
     FROM 계약
    GROUP BY 상품코드
   HAVING 설계사 = '서윤';

㉣ SELECT 상품코드, COUNT(DISTINCT 설계사)
     FROM 계약
    GROUP BY 상품코드;
1㉡
2㉢
3㉠
4㉣
정답 및 해설

HAVING 은 그룹마다 한 번 평가되므로 GROUP BY 열이나 집계 함수만 쓸 수 있는데, ㉢의 설계사는 그룹화되지 않은 열이라 오류가 난다.

설계사로 거르려면 그룹화 전에 처리되는 WHERE 절로 옮겨야 한다.

  • ㉠은 그룹화 열인 상품코드를 HAVING 에 써서 정상 실행된다.
  • ㉡은 GROUP BY 열을 SELECT 에 쓰지 않아도 되므로 4행이 나온다.
  • ㉣은 그룹별로 서로 다른 설계사 수를 세는 정상 집계다.
문제 22
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호등급보험료
1A50
2(null)30
3B70
4A90
5(null)60
6B40
SELECT 계약번호, 등급, 보험료
  FROM 계약
 ORDER BY 등급 DESC NULLS LAST, 보험료 DESC;
1
계약번호등급보험료
6B40
3B70
1A50
4A90
2(null)30
5(null)60
2
계약번호등급보험료
3B70
6B40
4A90
1A50
5(null)60
2(null)30
3
계약번호등급보험료
4A90
1A50
3B70
6B40
5(null)60
2(null)30
4
계약번호등급보험료
5(null)60
2(null)30
3B70
6B40
4A90
1A50
정답 및 해설

Oracle 은 NULL 을 가장 큰 값으로 보아 DESC 에서 맨 앞에 두지만 NULLS LAST 를 지정했으므로 B→A→NULL 순이 된다.

같은 등급 안에서는 두 번째 키 보험료 DESC 로 B(70, 40)·A(90, 50)·NULL(60, 30) 순으로 정렬된다.

  • NULL 이 맨 앞인 표는 NULLS LAST 를 무시한 기본 DESC 정렬이다.
  • 같은 등급 안에서 보험료가 오르는 표는 두 번째 키를 오름차순으로 보았다.
  • A 등급이 먼저인 표는 등급을 오름차순으로 정렬했다.
문제 23
계약 테이블은 6행(상태 '유지' 4행, '해지' 2행), 특약 테이블은 3행(그중 1행은 특약명이 NULL)이다. 다음 SQL 의 결과 행 수는?
SELECT K.계약번호, T.특약명
  FROM 계약 K CROSS JOIN 특약 T
 WHERE K.상태 = '유지';
112
28
318
46
정답 및 해설

CROSS JOIN 은 조인 조건 없이 모든 행을 짝짓는 카티션 곱이고, WHERE 가 계약 쪽만 '유지' 4행으로 거르므로 4 × 3 = 12행이다.

특약명이 NULL 인 행도 곱하기에서 빠지지 않으며, 조건이 없는 한 NULL 여부는 행 수에 영향을 주지 않는다.

  • 8행은 특약명이 NULL 인 행을 곱하기에서 뺀 결과다.
  • 18행은 WHERE 필터 전 6 × 3 전체 곱이다.
  • 6행은 계약 쪽 행 수만 남는다고 본 결과다.
문제 24
다음 SQL 의 실행 결과로 옳은 것은?

[고객]

고객번호고객명
1하늘
2바다
3구름

[계약]

계약번호고객번호상품명
101암보험
111실손
122운전자
13(null)단체상해
143치아

[청구]

청구번호계약번호청구액
10010300
1011150
1021180
10313120
SELECT C.고객명, K.상품명, Q.청구액
  FROM 고객 C, 계약 K, 청구 Q
 WHERE C.고객번호 = K.고객번호
   AND K.계약번호 = Q.계약번호
 ORDER BY Q.청구번호;
1
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
(null)단체상해120
2
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
바다운전자(null)
3
고객명상품명청구액
하늘암보험300
하늘실손50
하늘실손80
4
고객명상품명청구액
하늘암보험300
하늘실손50
정답 및 해설

세 테이블을 N-1 = 2개의 조인 조건으로 모두 이었고 INNER 조인이라 세 테이블 모두에 짝이 있는 청구 100·101·102 세 행만 남는다.

실손 계약에 청구가 둘이라 하늘·실손이 두 번 나오며, 고객번호가 NULL 인 단체상해와 청구 없는 운전자·치아는 빠진다.

  • 2행 표는 한 계약에 청구가 여러 건이면 한 행만 남는다고 보았다.
  • 단체상해 행이 있는 표는 NULL 고객번호도 조인된다고 보았다.
  • 운전자 행이 있는 표는 OUTER 조인처럼 짝 없는 계약을 남겼다.
문제 25
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호상품명
10암보험
11실손
12운전자
13치아

[청구]

청구번호계약번호상태
10010지급
10111반려
10211지급
10312반려
SELECT K.계약번호, K.상품명, Q.청구번호
  FROM 계약 K LEFT OUTER JOIN 청구 Q
    ON K.계약번호 = Q.계약번호
 WHERE Q.상태 <> '반려'
 ORDER BY K.계약번호, Q.청구번호;
1
계약번호상품명청구번호
10암보험100
11실손102
12운전자(null)
13치아(null)
2
계약번호상품명청구번호
10암보험100
11실손101
11실손102
13치아(null)
3
계약번호상품명청구번호
10암보험100
11실손102
4
계약번호상품명청구번호
10암보험100
11실손102
13치아(null)
정답 및 해설

조인 뒤 WHERE 가 적용되므로 반려 청구 101·103 행이 빠지고, 청구가 없어 상태가 NULL 인 치아 행도 NULL <> '반려' 가 UNKNOWN 이라 제거되어 2행만 남는다.

결국 바깥 조인의 보존 효과가 사라져 INNER 조인과 같아지며, 기준 행을 살리려면 이 조건을 ON 절로 옮겨야 한다.

  • 치아 행이 남은 표는 NULL 이 <> 조건을 통과한다고 보았다.
  • 운전자·치아가 NULL 로 남은 표는 조건을 ON 절에 둔 결과다.
  • 101 행이 남은 표는 WHERE 조건을 적용하지 않았다.
문제 26
다음 SQL 의 실행 결과로 옳은 것은?

[계약]

계약번호상품코드채널보험료
1P1대면50
2P1온라인30
3P2(null)40
4P2대면45
5P3온라인20

[상품]

상품코드채널상품명
P1대면종신A
P1온라인종신B
P2대면실손A
P3TM암보험
SELECT *
  FROM 계약 NATURAL JOIN 상품
 ORDER BY 계약번호;
1
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2(null)340실손A
P2대면445실손A
2
계약번호상품코드채널보험료상품명
1P1대면50종신A
2P1온라인30종신B
4P2대면45실손A
3
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2대면445실손A
P3온라인520(null)
4
상품코드채널계약번호보험료상품명
P1대면150종신A
P1온라인230종신B
P2대면445실손A
정답 및 해설

NATURAL JOIN 은 이름이 같은 상품코드·채널 두 열이 모두 같은 행만 결합하고, SELECT * 에서 공통 열을 맨 앞에 한 번만 표시한다.

계약 3은 채널이 NULL 이라 등치 비교가 UNKNOWN 이 되어 빠지고, 계약 5는 (P3, 온라인) 짝이 상품에 없어 빠지므로 계약 1·2·4 세 행이 남는다.

  • 계약 3을 실손A 와 결합한 표는 NULL 인 공통 열을 비교에서 건너뛴다고 본 오해이다.
  • 계약번호를 맨 앞에 둔 표는 공통 열이 앞으로 모인다는 규칙을 놓쳤다.
  • 계약 5를 상품명 NULL 로 남긴 표는 외부 조인처럼 처리한 결과이다.
문제 27
Oracle 쿼리 ㉮와 같은 [결과]를 ANSI 쿼리 ㉯로 얻으려 한다. ( ㉠ )에 들어갈 구문으로 옳은 것은?

[계약]

계약번호고객명상태
101김하늘유지
102이도윤해지
103박서아유지
104최로운유지

[청구]

청구번호계약번호금액
1101120
2102300
310380
410350
510970

㉮

SELECT R.청구번호, C.고객명
  FROM 청구 R, 계약 C
 WHERE C.계약번호(+) = R.계약번호
   AND C.상태(+) = '유지'
 ORDER BY R.청구번호;

㉯

SELECT R.청구번호, C.고객명
  FROM 청구 R ( ㉠ ) 계약 C
    ON C.계약번호 = R.계약번호
   AND C.상태 = '유지'
 ORDER BY R.청구번호;

[결과]

청구번호고객명
1김하늘
2(null)
3박서아
4박서아
5(null)
1INNER JOIN
2LEFT OUTER JOIN
3RIGHT OUTER JOIN
4CROSS JOIN
정답 및 해설

(+) 가 붙은 계약 쪽은 짝이 없을 때 NULL 로 채워지는 쪽이므로, 보존할 청구를 왼쪽에 둔 LEFT OUTER JOIN 이 같은 결과를 낸다.

C.상태(+) = '유지' 처럼 (+) 가 붙은 조건은 ON 절 조건이 되어, 해지 계약의 청구 2와 계약이 없는 청구 5도 고객명 NULL 로 남는다.

  • RIGHT OUTER JOIN 은 계약을 보존해 102·104가 청구 NULL 로 나오고 청구 2·5는 빠진다.
  • INNER JOIN 은 짝이 맞는 청구 세 건만 남겨 NULL 행이 사라진다.
  • CROSS JOIN 은 조건 없이 모든 행을 곱하는 구문이라 ON 절을 붙이면 오류가 난다.
문제 28
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호계약번호청구금액
120150
220180
320260
420260
520390
6204(null)
720440
SELECT 청구번호, 계약번호, 청구금액
  FROM 청구 A
 WHERE 청구금액 = (SELECT MAX(청구금액)
                     FROM 청구 B
                    WHERE B.계약번호 = A.계약번호)
 ORDER BY 청구번호;
1
청구번호계약번호청구금액
220180
320260
520390
720440
2
청구번호계약번호청구금액
220180
320260
420260
520390
3
청구번호계약번호청구금액
220180
320260
420260
520390
720440
4
청구번호계약번호청구금액
220180
320260
420260
520390
6204(null)
720440
정답 및 해설

연관 서브쿼리는 바깥 행 A 의 계약번호를 받아 같은 계약 안의 MAX(청구금액)을 매번 다시 구하므로, 계약마다 최대 금액과 같은 청구가 모두 남는다.

202는 60이 두 건이라 두 건 모두 남고, 204는 MAX 가 NULL 을 무시해 40이 최대가 되며 금액이 NULL 인 청구 6은 = 비교가 UNKNOWN 이라 빠진다.

  • 202에서 한 건만 남긴 표는 동점을 하나로 줄인 것으로, = 비교는 같은 값을 모두 통과시킨다.
  • 204가 빠진 표는 NULL 이 섞이면 MAX 가 NULL 이 된다고 본 오해이다.
  • 청구 6을 넣은 표는 NULL 금액도 최댓값 비교를 통과한다고 본 것이다.
문제 29
계약별 청구 합계를 구해 다음 [결과]를 얻으려 한다. ( ㉠ )에 들어갈 식으로 옳은 것은?

[계약]

계약번호고객명
301한지우
302오세린
303문태오
304신가람

[청구]

청구번호계약번호금액
1301100
2301(null)
330270
4304(null)
SELECT C.계약번호, C.고객명,
       (SELECT ( ㉠ )
          FROM 청구 R
         WHERE R.계약번호 = C.계약번호) AS 청구합계
  FROM 계약 C
 ORDER BY C.계약번호;

[결과]

계약번호고객명청구합계
301한지우100
302오세린70
303문태오0
304신가람0
1SUM(R.금액)
2SUM(COALESCE(R.금액, 0))
3COUNT(R.금액)
4COALESCE(SUM(R.금액), 0)
정답 및 해설

집계 함수만 있는 스칼라 서브쿼리는 대상 행이 없어도 한 행을 돌려주므로, 그 SUM 결과를 COALESCE(SUM(R.금액), 0) 으로 감싸야 청구가 없는 303도 0이 된다.

301은 NULL 을 뺀 100, 302는 70이고, 금액이 NULL 뿐인 304와 청구가 없는 303은 SUM 이 NULL 이어서 0으로 바뀐다.

  • SUM(R.금액) 만 쓰면 303·304가 NULL 로 나온다.
  • SUM(COALESCE(R.금액, 0)) 은 304는 0이지만 대상 행이 없는 303은 여전히 NULL 이다.
  • COUNT(R.금액) 는 금액이 아닌 건수를 세어 301·302가 1이 된다.
문제 30
청구가 한 건도 없는 계약을 찾는 다음 SQL 이 0행을 반환했다. 청구.계약번호에 NULL 이 있기 때문일 때, 해결 방법으로 적절하지 않은 것은?
SELECT 계약번호
  FROM 계약
 WHERE 계약번호 NOT IN (SELECT 계약번호 FROM 청구);
1<> ALL 로 바꾼다
2NOT EXISTS 로 바꾼다
3서브쿼리에 IS NOT NULL 을 건다
4LEFT JOIN 후 IS NULL 로 거른다
정답 및 해설

<> ALL 은 NOT IN 과 같은 뜻이라 서브쿼리 결과에 NULL 이 있으면 비교가 UNKNOWN 이 되어 여전히 0행이다.

NOT IN (a, NULL) 은 x <> a AND x <> NULL 로 풀리는데, 뒤쪽이 항상 UNKNOWN 이라 전체가 참이 될 수 없다.

  • 서브쿼리에서 NULL 을 걸러 내면 비교 대상에 NULL 이 없어 정상 동작한다.
  • NOT EXISTS 는 행 존재 여부만 보므로 NULL 의 영향을 받지 않는다.
  • LEFT JOIN 후 청구 쪽 키가 NULL 인 행만 고르면 청구 없는 계약이 남는다.
문제 31
집합 연산자 UNION 과 UNION ALL 에 대한 설명으로 옳은 것은?
1UNION ALL 은 열 수가 달라도 된다
2UNION 은 NULL 끼리 다르게 본다
3합친 결과에 중복이 없으면 행 수가 같다
4UNION ALL 은 자동 정렬된다
정답 및 해설

UNION 은 합친 결과에서 중복 행을 제거하고 UNION ALL 은 그대로 두므로, 합친 결과에 중복 행이 없다면 두 연산의 행 수는 같다.

이 경우 중복 제거를 위한 정렬·비교 작업이 없는 UNION ALL 이 같은 결과를 더 적은 부하로 낸다.

  • UNION ALL 은 중복 제거도 정렬도 하지 않으며 순서는 ORDER BY 로만 정해진다.
  • UNION 은 중복을 판단할 때 NULL 끼리를 같은 값으로 본다.
  • 집합 연산은 모든 SELECT 의 열 개수와 데이터 형이 맞아야 한다.
문제 32
다음 SQL 의 실행 결과로 옳은 것은? (EXCEPT 는 Oracle 의 MINUS 와 같다)

[계약]

계약번호고객번호상품코드
1C1P1
2C1P1
3C1P2
4C2P1
5C3(null)
6C4P3

[청구]

청구번호고객번호상품코드
1C1P2
2C2P1
3C3(null)
4C5P3
SELECT 고객번호, 상품코드 FROM 계약
EXCEPT
SELECT 고객번호, 상품코드 FROM 청구
 ORDER BY 고객번호, 상품코드;
1
고객번호상품코드
C5P3
2
고객번호상품코드
C1P1
C4P3
3
고객번호상품코드
C1P1
C1P1
C4P3
4
고객번호상품코드
C1P1
C3(null)
C4P3
정답 및 해설

EXCEPT(MINUS) 는 위 결과에서 아래 결과에 있는 행을 빼고 중복을 제거하므로 (C1, P1) 과 (C4, P3) 두 행만 남는다.

(C1, P2)·(C2, P1)·(C3, NULL) 은 청구에도 있어 빠지는데, 집합 연산은 NULL 끼리를 같은 값으로 비교한다.

  • (C1, P1) 을 두 행 남긴 표는 EXCEPT ALL 처럼 중복을 유지한 결과이다.
  • (C3, NULL) 을 남긴 표는 NULL 끼리를 다른 값으로 본 오해이다.
  • (C5, P3) 만 있는 표는 두 SELECT 의 순서를 바꿔 청구에서 계약을 뺀 결과이다.
문제 33
다음 SQL 의 결과 행 수는?

[청구]

청구번호유형채널금액
1입원앱300
2입원창구500
3통원앱40
4통원앱60
5통원(null)30
6수술창구900
7입원앱200
SELECT 유형, 채널, SUM(금액) AS 합계
  FROM 청구
 GROUP BY CUBE (유형, 채널);
111
210
39
412
정답 및 해설

CUBE (유형, 채널) 는 (유형, 채널)·(유형)·(채널)·() 네 가지 그룹을 모두 만들므로 상세 5 + 유형 소계 3 + 채널 소계 3 + 총계 1 = 12행이다.

채널이 NULL 인 통원 청구는 상세 그룹 (통원, NULL) 과 채널 NULL 소계를 따로 만들며, 이 소계는 총계와 합쳐지지 않는다.

  • 9행은 ROLLUP 처럼 유형 소계와 총계만 센 값이다.
  • 11행은 채널 NULL 소계를 총계와 같은 행으로 본 값이다.
  • 10행은 채널이 NULL 인 데이터를 상세와 소계 모두에서 뺀 값이다.
문제 34
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호유형채널금액
1입원앱100
2입원(null)50
3통원앱30
4통원앱20
SELECT 유형, 채널, SUM(금액) AS 합계,
       GROUPING(채널) AS G
  FROM 청구
 GROUP BY GROUPING SETS ((유형, 채널), (유형))
 ORDER BY 유형, G, 채널;
1
유형채널합계G
입원앱1000
입원(null)500
입원(null)1001
통원앱500
통원(null)501
2
유형채널합계G
입원앱1000
입원(null)500
입원(null)1501
통원앱500
통원(null)501
(null)(null)2001
3
유형채널합계G
입원앱1000
입원(null)1500
입원(null)501
통원앱500
통원(null)501
4
유형채널합계G
입원앱1000
입원(null)500
입원(null)1501
통원앱500
통원(null)501
정답 및 해설

GROUPING SETS ((유형, 채널), (유형)) 은 유형·채널 상세와 유형 소계만 만들고 총계는 만들지 않으며, 소계 행의 GROUPING(채널) 만 1이다.

입원의 NULL 채널 50은 원래 데이터의 NULL 이라 G=0 인 상세 행이고, 입원 소계 150은 NULL 채널 금액까지 더한 값이다.

  • 원래 NULL 행에 1을 준 표는 GROUPING 이 집계로 생긴 NULL 에만 1을 준다는 점을 놓쳤다.
  • 총계 행이 있는 표는 () 를 지정하지 않았는데도 전체 합계를 넣었다.
  • 입원 소계를 100으로 쓴 표는 채널이 NULL 인 행을 소계에서 뺀 것이다.
문제 35
다음 [결과]를 얻기 위해 ( ㉠ )에 들어갈 구문으로 옳은 것은?

[계약]

계약번호지점상품군보험료
1강남종신30
2강남실손10
3분당종신20
4분당실손15
5분당실손5
SELECT 지점, 상품군, SUM(보험료) AS 합계
  FROM 계약
 GROUP BY ( ㉠ )
 ORDER BY 상품군, 지점;

[결과]

지점상품군합계
강남실손10
분당실손20
(null)실손30
강남종신30
분당종신20
(null)종신50
(null)(null)80
1ROLLUP (지점, 상품군)
2ROLLUP (상품군, 지점)
3지점, ROLLUP (상품군)
4CUBE (상품군, 지점)
정답 및 해설

[결과]에는 상품군별 소계(지점 NULL)와 총계만 있으므로, 상품군을 상위 계층으로 둔 ROLLUP (상품군, 지점) 이 맞다.

ROLLUP 은 인수 순서대로 (상품군, 지점), (상품군), () 단계로 집계하며 SELECT 절 열 순서와는 무관하다.

  • ROLLUP (지점, 상품군) 은 지점별 소계를 만들어 강남·분당 소계 행이 나온다.
  • CUBE 는 상품군 소계와 지점 소계를 모두 만들어 행이 더 많다.
  • 지점, ROLLUP (상품군) 은 지점별 소계만 만들고 총계 행이 없다.
문제 36
다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객번호금액
1K1100
2K150
3K2200
4K3(null)
5K330
6K220
SELECT 고객번호, SUM(금액) AS 합계,
       COUNT(*) OVER () AS 고객수,
       SUM(SUM(금액)) OVER () AS 전체
  FROM 청구
 GROUP BY 고객번호
 ORDER BY 고객번호;
1
고객번호합계고객수전체
K11506400
K22206400
K3306400
2
고객번호합계고객수전체
K11503400
K22203400
K3303400
3
고객번호합계고객수전체
K11503150
K22203370
K3303400
4
고객번호합계고객수전체
K11503(null)
K22203(null)
K3(null)3(null)
정답 및 해설

윈도우 함수는 GROUP BY 가 끝난 뒤 그룹 결과 3행 위에서 계산되므로, 행 수는 고객 수인 3행이고 COUNT(*) OVER () 도 3이다.

SUM(SUM(금액)) OVER () 는 고객별 합계 150·220·30을 모두 더한 400을 각 행에 붙이며, K3 합계는 NULL 을 뺀 30이다.

  • 고객수를 6으로 쓴 표는 윈도우 함수가 원본 행에 적용된다고 본 오해이다.
  • K3 와 전체를 NULL 로 쓴 표는 집계 함수가 NULL 을 무시한다는 점을 놓쳤다.
  • 전체를 누적 값으로 쓴 표는 ORDER BY 가 없는 OVER () 를 누적 프레임으로 본 것이다.
문제 37
지점별 신계약 건수 순위로 다음 [결과]를 얻으려 한다. ( ㉠ )에 들어갈 함수로 옳은 것은?

[실적]

설계사지점건수
윤아강남9
도현강남9
시우강남8
하린강남7
지안판교6
서준판교5
예린판교5
SELECT 지점, 설계사, 건수
  FROM (SELECT 지점, 설계사, 건수,
               ( ㉠ ) OVER (PARTITION BY 지점
                            ORDER BY 건수 DESC) AS 순위
          FROM 실적) T
 WHERE 순위 <= 2
 ORDER BY 지점, 건수 DESC, 설계사;

[결과]

지점설계사건수
강남도현9
강남윤아9
강남시우8
판교지안6
판교서준5
판교예린5
1NTILE(2)
2RANK()
3ROW_NUMBER()
4DENSE_RANK()
정답 및 해설

[결과]에서 강남은 9건 두 명 뒤의 8건까지, 판교는 6건과 동점인 5건 두 명이 모두 나오므로 동점 뒤 순위를 건너뛰지 않는 DENSE_RANK() 가 맞다.

강남 순위는 DENSE_RANK 로 1·1·2·3, RANK 로 1·1·3·4 가 되어 동점 뒤 순위 처리에서 결과가 갈린다.

  • RANK() 는 강남 8건이 3위가 되어 강남에서 9건 두 명만 남는다.
  • ROW_NUMBER() 는 동점에도 고유 번호를 주어 판교 5건 중 한 명이 빠진다.
  • NTILE(2) 는 행을 두 묶음으로 나눌 뿐이라 모든 행이 2 이하가 되어 하린까지 나온다.
문제 38
다음 SQL 의 실행 결과로 옳은 것은?

[납입]

계약번호회차납입액
A011100
A012(null)
A013100
B02150
B02270
SELECT 계약번호, 회차, 납입액,
       SUM(납입액) OVER (PARTITION BY 계약번호
                         ORDER BY 회차 DESC) AS 잔여합
  FROM 납입
 ORDER BY 계약번호, 회차;
1
계약번호회차납입액잔여합
A011100100
A012(null)100
A013100200
B0215050
B02270120
2
계약번호회차납입액잔여합
A011100200
A012(null)200
A013100200
B02150120
B02270120
3
계약번호회차납입액잔여합
A011100(null)
A012(null)(null)
A013100100
B02150120
B0227070
4
계약번호회차납입액잔여합
A011100200
A012(null)100
A013100100
B02150120
B0227070
정답 및 해설

OVER 안의 ORDER BY 회차 DESC 때문에 누적은 계약별 마지막 회차부터 거꾸로 쌓이고, 출력 순서는 바깥 ORDER BY 에 따라 회차 오름차순이 된다.

A01은 3회차 100, 2회차는 NULL 을 건너뛴 누적이라 100, 1회차 200이며 B02는 2회차 70, 1회차 120이다.

  • 1회차부터 쌓은 표는 OVER 의 정렬 방향을 바깥 정렬과 같다고 본 것이다.
  • NULL 이후를 NULL 로 쓴 표는 SUM 이 NULL 을 건너뛴다는 점을 놓쳤다.
  • 계약별 총합을 모든 행에 쓴 표는 OVER 안의 ORDER BY 를 무시한 결과이다.
문제 39
다음 SQL 의 실행 결과로 옳은 것은?

[월별청구]

월금액
140
270
3(null)
420
560
SELECT 월, 금액,
       MAX(금액) OVER (ORDER BY 월
                       ROWS BETWEEN 1 PRECEDING
                                AND 1 FOLLOWING) AS 이웃최대,
       LEAD(금액, 1, 0) OVER (ORDER BY 월) AS 다음
  FROM 월별청구
 ORDER BY 월;
1
월금액이웃최대다음
1407070
270700
3(null)7020
4206060
560600
2
월금액이웃최대다음
1407070
27070(null)
3(null)7020
4206060
560600
3
월금액이웃최대다음
1407070
27070(null)
3(null)7020
4206060
56060(null)
4
월금액이웃최대다음
1404070
27070(null)
3(null)7020
4207060
560700
정답 및 해설

ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 은 앞·현재·뒤 한 행씩 세 행 중 최댓값을 구하므로 4월은 NULL·20·60 중 60이다.

LEAD(금액, 1, 0) 의 기본값 0은 다음 행이 없는 5월에만 쓰이고, 2월은 다음 행 금액이 실제 NULL 이라 NULL 이 나온다.

  • 2월 다음을 0으로 쓴 표는 기본값이 NULL 값까지 대신한다고 본 오해이다.
  • 이웃최대가 40·70·70·70·70 인 표는 처음부터 현재 행까지의 누적 프레임으로 계산했다.
  • 5월 다음을 NULL 로 쓴 표는 세 번째 인수인 기본값을 무시했다.
문제 40
Oracle(12c 이상)에서 보험료 상위 4~6위 계약을 조회하려 한다. 보험료에 NULL 과 동점이 없을 때, 실행은 되지만 결과가 항상 0행인 SQL 은?

㉠

SELECT 계약번호, 보험료
  FROM (SELECT ROWNUM AS RN, T.*
          FROM (SELECT * FROM 계약
                 ORDER BY 보험료 DESC) T
         WHERE ROWNUM <= 6)
 WHERE RN >= 4;

㉡

SELECT 계약번호, 보험료
  FROM (SELECT * FROM 계약
         ORDER BY 보험료 DESC)
 WHERE ROWNUM BETWEEN 4 AND 6;

㉢

SELECT 계약번호, 보험료
  FROM 계약
 ORDER BY 보험료 DESC
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;

㉣

SELECT 계약번호, 보험료
  FROM (SELECT 계약번호, 보험료,
               ROW_NUMBER() OVER (ORDER BY 보험료 DESC) AS RN
          FROM 계약)
 WHERE RN BETWEEN 4 AND 6;
1㉣
2㉡
3㉢
4㉠
정답 및 해설

ROWNUM 은 행을 꺼낼 때 1부터 차례로 붙으므로, 첫 행이 ROWNUM 4 이상 조건에서 탈락하면 다음 행도 다시 1을 받아 계속 탈락해 ROWNUM BETWEEN 4 AND 6 은 항상 0행이다.

4~6위를 얻으려면 ROWNUM 을 인라인 뷰 안에서 별칭(RN)으로 고정한 뒤 바깥에서 비교해야 한다.

  • 정렬한 인라인 뷰에서 ROWNUM 을 RN 으로 고정한 뒤 RN >= 4 로 거르면 4~6위가 나온다.
  • OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY 는 앞 3행을 건너뛰고 다음 3행을 가져온다.
  • ROW_NUMBER 로 매긴 RN 을 바깥에서 비교하면 4~6위가 정확히 나온다.
문제 41
SQL Server 에서 다음 SQL 의 결과 행 수는?

[계약]

계약번호고객명보험료
1가윤90
2나은80
3다인70
4라희70
5마루60
6바다50
7사랑40
SELECT TOP 30 PERCENT WITH TIES 계약번호, 보험료
  FROM 계약
 ORDER BY 보험료 DESC;
13
22
35
44
정답 및 해설

7행의 30%는 2.1행인데 TOP n PERCENT 는 소수 행을 올림해 3행을 고르고, WITH TIES 가 세 번째 행과 보험료가 같은 행을 더해 4행이 된다.

보험료 내림차순으로 90·80·70 다음에 같은 70인 계약이 동점으로 함께 나온다.

  • 2행은 2.1을 내림하고 동점도 무시한 값이다.
  • 3행은 올림은 했지만 WITH TIES 를 무시한 값이다.
  • 5행은 동점이 아닌 60까지 포함한 값이다.
문제 42
Oracle 에서 다음 SQL 을 실행했을 때, 코드가 D 인 행의 값으로 옳지 않은 것은?

[조직]

코드상위
A(null)
BA
CA
DB
SELECT LEVEL, 코드, PRIOR 코드 AS 부모,
       CONNECT_BY_ROOT 코드 AS 루트,
       CONNECT_BY_ISLEAF AS 말단
  FROM 조직
 START WITH 상위 IS NULL
CONNECT BY PRIOR 코드 = 상위;
1CONNECT_BY_ROOT 코드 = A
2PRIOR 코드 = A
3LEVEL 값 = 3
4CONNECT_BY_ISLEAF = 1
정답 및 해설

SELECT 절의 PRIOR 코드 는 현재 행의 부모 행 값을 돌려주므로, D 행에서는 부모인 B 가 나오고 A 가 아니다.

D 는 A→B→D 경로의 세 번째 단계라 LEVEL 이 3이고, 자식이 없어 CONNECT_BY_ISLEAF 는 1이다.

  • LEVEL 은 루트를 1로 시작해 한 단계 내려갈 때마다 1씩 늘어난다.
  • D 아래에는 자식 행이 없으므로 말단 표시가 1이다.
  • CONNECT_BY_ROOT 는 그 행이 속한 계층의 루트인 A 를 돌려준다.
문제 43
Oracle 에서 다음 SQL 의 실행 결과로 옳은 것은?

[추천]

고객추천인
K1K3
K2K1
K3K2
K4K2
SELECT LEVEL, 고객, CONNECT_BY_ISCYCLE AS 순환
  FROM 추천
 START WITH 고객 = 'K1'
CONNECT BY NOCYCLE PRIOR 고객 = 추천인
 ORDER SIBLINGS BY 고객;
1
LEVEL고객순환
1K10
2K20
3K31
3K40
2
LEVEL고객순환
1K10
2K20
3K31
3
LEVEL고객순환
1K10
2K20
3K31
4K10
3K40
4
LEVEL고객순환
1K11
2K20
3K30
3K40
정답 및 해설

NOCYCLE 은 K3 의 자식으로 이미 조상인 K1 이 다시 나오는 순환을 만나도 오류 없이 그 행을 내지 않고, 순환을 일으킨 K3 에 CONNECT_BY_ISCYCLE 1을 표시한다.

K1→K2 다음 K2 의 자식 K3·K4 가 형제로 ORDER SIBLINGS BY 고객 순서에 따라 나오며, 자식이 없는 K4 는 순환이 아니므로 0이다.

  • K4 가 빠진 표는 순환을 만나면 전체 전개가 멈춘다고 본 오해이다.
  • K1 에 1을 준 표는 다시 방문될 행에 순환 표시가 붙는다고 본 것이다.
  • LEVEL 4의 K1 을 넣은 표는 NOCYCLE 이 순환 행을 한 번 더 보여 준다고 본 것이다.
문제 44
Oracle 에서 다음 SQL 의 실행 결과로 옳은 것은?

[청구]

청구번호고객유형금액
1정민입원300
2정민통원40
3하늘통원60
4정민통원20
SELECT *
  FROM 청구
 PIVOT (SUM(금액) FOR 유형 IN ('입원' AS 입원, '통원' AS 통원))
 ORDER BY 청구번호;
1
청구번호고객유형입원통원
1정민입원300(null)
2정민통원(null)40
3하늘통원(null)60
4정민통원(null)20
2
청구번호고객입원통원
1정민300(null)
2정민(null)40
3하늘(null)60
4정민(null)20
3
고객입원통원
정민30060
하늘(null)60
4
청구번호고객입원통원
1정민3000
2정민040
3하늘060
4정민020
정답 및 해설

PIVOT 은 집계 대상(금액)과 FOR 열(유형)을 뺀 나머지 모든 열로 암묵적 GROUP BY 하므로, 청구번호가 남아 있는 이 쿼리는 원본 4행이 그대로 4행이 된다.

각 행은 자기 유형 칸에만 금액이 들어가고 다른 칸은 NULL 이며, 고객별로 합치려면 인라인 뷰에서 청구번호를 빼고 PIVOT 해야 한다.

  • 고객별 2행으로 합친 표는 청구번호가 그룹 기준이 된다는 점을 놓쳤다.
  • 빈 칸을 0으로 쓴 표는 해당 값이 없을 때 SUM 이 NULL 을 돌려준다는 점을 놓쳤다.
  • 유형 열이 남은 표는 FOR 절의 열이 결과 열로 바뀌며 사라진다는 점을 놓쳤다.
문제 45
Oracle 에서 다음 SQL 의 결과 값은?

[계약]

계약번호증권번호
1LF-2026
2NL2027
3lf-2026
4LFNL-2026
5NL-20261
6NL-0007
7LF-202
SELECT COUNT(*)
  FROM 계약
 WHERE REGEXP_LIKE(증권번호, '^(LF|NL)-?\d{4}$');
14
23
31
42
정답 및 해설

패턴은 LF 또는 NL 로 시작해 하이픈이 0~1개(-?) 온 뒤 숫자 정확히 4개로 끝나야 하므로 LF-2026, NL2027, NL-0007 세 행이 일치한다.

REGEXP_LIKE 는 기본으로 대소문자를 구분하고, ^ 와 $ 앵커가 앞뒤에 다른 문자가 붙는 것을 막는다.

  • lf-2026 은 소문자라 LF 와 일치하지 않는다.
  • LFNL-2026 은 선택 그룹이 한 번만 와야 하는데 LF 뒤에 NL 이 붙어 탈락한다.
  • NL-20261 과 LF-202 는 숫자가 4개가 아니어서 끝 조건을 만족하지 못한다.
문제 46
[해지계약] 테이블이 [계약]과 같은 구조의 빈 테이블일 때, 다음 문장을 실행한 뒤 (계약 행 수, 해지계약 행 수)로 옳은 것은?

[계약]

계약번호상태보험료
1유지30
2해지20
3유지45
4(null)15
5해지25
6유지10
INSERT INTO 해지계약
SELECT * FROM 계약 WHERE 상태 = '해지';
DELETE FROM 계약 WHERE 상태 = '해지';
COMMIT;
14, 2
26, 2
33, 2
44, 0
정답 및 해설

INSERT … SELECT 는 조건에 맞는 해지 2행을 해지계약에 복사하고, 이어진 DELETE 가 계약에서 같은 2행을 지워 (4, 2) 가 된다.

상태가 NULL 인 계약은 = '해지' 비교가 UNKNOWN 이라 복사도 삭제도 되지 않고 계약에 남는다.

  • (3, 2) 는 상태가 NULL 인 행까지 삭제된다고 본 값이다.
  • (4, 0) 은 INSERT … SELECT 가 행을 넣지 못한다고 본 값이다.
  • (6, 2) 는 DELETE 의 결과를 반영하지 않은 값이다.
문제 47
ANSI 표준 격리 수준 기준으로, 다음 T1 에서 일어난 현상을 막을 수 있는 가장 낮은 격리 수준은?
T1: SELECT 금액 FROM 청구 WHERE 청구번호 = 7;   -- 300
T2: UPDATE 청구 SET 금액 = 450 WHERE 청구번호 = 7;
T2: COMMIT;
T1: SELECT 금액 FROM 청구 WHERE 청구번호 = 7;   -- 450
1READ COMMITTED
2SERIALIZABLE
3어느 수준으로도 막을 수 없다
4REPEATABLE READ
정답 및 해설

T1 이 이미 읽은 행을 T2 가 수정·커밋해 같은 조회의 값이 달라졌으므로 Non-Repeatable Read 이고, 이를 막는 가장 낮은 수준은 REPEATABLE READ 이다.

REPEATABLE READ 는 트랜잭션이 읽은 행이 끝날 때까지 바뀌지 않게 보장하며, 새 행이 나타나는 팬텀까지 막으려면 SERIALIZABLE 이 필요하다.

  • 격리 수준을 REPEATABLE READ 이상으로 올리면 이 현상은 막을 수 있다.
  • READ COMMITTED 는 Dirty Read 만 막고 커밋된 수정은 다시 읽을 때 보인다.
  • SERIALIZABLE 로도 막히지만 가장 낮은 수준은 아니다.
문제 48
Oracle 에서 다음 [청구] 테이블에 실행했을 때 오류가 발생하는 문장은?
CREATE TABLE 청구 (
  청구번호 NUMBER(5),
  금액     NUMBER(9),
  사유     VARCHAR2(30)
);
-- 3행 입력·커밋, 모든 행의 금액·사유에 값이 있음
1ALTER TABLE 청구 MODIFY (금액 VARCHAR2(9))
2ALTER TABLE 청구 RENAME TO 청구이력
3ALTER TABLE 청구 DROP COLUMN 사유
4ALTER TABLE 청구 ADD (메모 VARCHAR2(100))
정답 및 해설

값이 들어 있는 열은 데이터 형을 바꿀 수 없으므로, 숫자 열인 금액을 VARCHAR2 로 MODIFY 하면 열이 비어 있어야 한다는 오류가 난다.

형을 바꾸려면 열 값을 모두 NULL 로 비우거나, 새 열을 ADD 해 값을 옮긴 뒤 기존 열을 지워야 한다.

  • 새 열 추가는 기존 행에 NULL 이 채워지므로 데이터가 있어도 된다.
  • RENAME TO 는 테이블 이름을 바꾸는 구문으로 정상 실행된다.
  • DROP COLUMN 은 데이터가 있어도 열과 값을 함께 지운다.
문제 49
다음 정의와 데이터에서 DELETE 문을 실행한 뒤 (계약 행 수, 청구 행 수)로 옳은 것은?

[계약]

계약번호고객명
501윤채원
502장태민
503홍유나

[청구]

청구번호계약번호금액
1501100
250240
350160
450390
550230
CREATE TABLE 청구 (
  청구번호 INT PRIMARY KEY,
  계약번호 INT REFERENCES 계약(계약번호)
           ON DELETE CASCADE,
  금액     INT
);

DELETE FROM 계약 WHERE 계약번호 = 501;
12, 4
23, 5
32, 3
42, 5
정답 및 해설

ON DELETE CASCADE 는 부모 행이 삭제될 때 그 행을 참조하는 자식 행도 함께 삭제하므로, 계약 501과 그 청구 2건이 지워져 (2, 3) 이 된다.

다른 계약을 참조하는 청구 3건은 영향을 받지 않고 그대로 남는다.

  • (2, 5) 는 자식 행을 남기고 FK 만 NULL 로 바꾸는 SET NULL 의 동작이다.
  • (3, 5) 는 자식이 있으면 부모 삭제를 막는 RESTRICT 의 동작이다.
  • (2, 4) 는 연쇄 삭제가 자식 한 건에만 적용된다고 본 값이다.
문제 50
Oracle 에서 다음 두 문장을 실행한 뒤의 설명으로 옳은 것은?
GRANT SELECT ON 청구 TO KIM WITH GRANT OPTION;
GRANT CREATE VIEW TO LEE WITH ADMIN OPTION;
1ADMIN OPTION 은 객체 권한용
2KIM 은 INSERT 도 할 수 있다
3KIM 은 SELECT 를 넘길 수 있다
4LEE 는 CREATE VIEW 를 못 준다
정답 및 해설

WITH GRANT OPTION 으로 객체 권한을 받은 KIM 은 청구의 SELECT 권한을 다른 사용자에게 다시 부여할 수 있다.

시스템 권한에는 WITH ADMIN OPTION 을 쓰며, 이를 받은 LEE 도 CREATE VIEW 를 다른 사용자에게 줄 수 있다.

  • LEE 는 ADMIN OPTION 을 받았으므로 CREATE VIEW 를 넘길 수 있다.
  • KIM 이 받은 권한은 SELECT 뿐이라 INSERT 는 할 수 없다.
  • ADMIN OPTION 은 시스템 권한용이고 객체 권한에는 GRANT OPTION 을 쓴다.