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

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

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

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

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

총 50문항 · 객관식 · 정답·해설 포함
문제 1
병원 진료 시스템을 설계할 때 물리적 데이터 모델링 단계에서 주로 수행하는 작업은?
1정규화로 속성을 정제한다
2핵심 엔터티와 관계를 도출한다
3인덱스와 저장 공간을 설계한다
4업무 범위를 추상화해 정의한다
정답 및 해설

물리적 모델링은 특정 DBMS 의 특성을 반영해 테이블·인덱스·파티션·저장 공간처럼 실제 구현에 필요한 구조를 설계하는 단계이다.

개념적 모델링은 핵심 엔터티와 관계를 추상화하고, 논리적 모델링은 키·속성·관계를 정의하며 정규화를 수행한다.

  • 핵심 엔터티와 관계 도출은 개념적 모델링의 작업이다.
  • 정규화로 속성을 정제하는 것은 논리적 모델링의 작업이다.
  • 업무 범위를 추상화해 정의하는 것은 개념적 모델링에 가깝다.
문제 2
다음 진료 ERD를 작성하는 절차와 방법에 대한 설명으로 적절하지 않은 것은?

그림

1관계를 설정한 뒤에 관계명을 기술한다
2관계 참여도는 관계명보다 먼저 정한다
3업무 중심 엔터티를 왼쪽 상단에 둔다
4엔터티 이름은 단수형 업무 용어로 쓴다
정답 및 해설

ERD 는 엔터티 도출·배치 → 관계 설정 → 관계명 기술 → 관계 참여도 기술 → 관계 필수 여부 기술 순서로 작성하므로 참여도는 관계명 다음에 정한다.

배치할 때는 업무의 중심 엔터티를 왼쪽 상단에 두고, 엔터티 이름은 현업 용어의 단수 명사로 붙인다.

  • 중심 엔터티를 왼쪽 상단에 두는 것은 권장 배치 방법이다.
  • 관계를 먼저 연결한 뒤 관계명을 적는 것이 작성 순서에 맞다.
  • 환자·진료처럼 단수형 업무 용어로 이름 붙이는 것은 명명 원칙에 맞다.
문제 3
진료 엔터티의 속성 이름을 정하는 방법으로 가장 적절한 것은?
1'JRYJ'처럼 약어로 짧게 줄여 쓴다
2엔터티마다 '일자'처럼 같은 이름을 쓴다
3'진료를 받은 날짜'처럼 서술형으로 쓴다
4업무 용어 '진료일자'처럼 명사로 쓴다
정답 및 해설

속성 이름은 해당 업무에서 쓰는 용어를 짧은 명사형으로 붙이는 것이 원칙이다.

서술식 이름과 약어는 의미 전달을 흐리고, 같은 이름이 여러 엔터티에 반복되면 혼동되므로 모델 전체에서 가급적 유일한 이름을 쓴다.

  • 서술식 이름은 속성명으로 쓰지 않는다.
  • 뜻을 알기 어려운 약어는 가능한 한 피한다.
  • 모든 엔터티에 같은 이름을 쓰면 의미가 구분되지 않는다.
문제 4
환자와 의사 사이의 M:N 관계를 '진료' 엔터티로 해소했을 때의 설명으로 옳은 것은?
1환자와 진료는 M:N 관계로 남는다
2진료는 환자의 속성으로 흡수된다
3의사와 진료는 1:1 관계가 된다
4진료는 환자·의사 양쪽의 M 쪽이 된다
정답 및 해설

M:N 관계는 교차 엔터티를 두어 1:M 관계 두 개로 바꾸며, 교차 엔터티는 양쪽 부모와의 관계에서 모두 M 쪽(자식)이 된다.

진료는 환자번호와 의사번호를 외래키로 받아 한 환자의 여러 진료, 한 의사의 여러 진료를 각각 표현한다.

  • 해소 후 환자와 진료는 1:M 관계가 된다.
  • 한 의사가 여러 진료를 맡으므로 1:1 이 아니라 1:M 이다.
  • 진료는 자체 속성을 가진 독립 엔터티로 남는다.
문제 5
다음 ERD에서 처방순번은 진료마다 1부터 매기는 일련번호이다. 처방 엔터티의 주식별자에 대한 설명으로 옳은 것은?

그림

1진료번호는 외부식별자가 아니다
2처방을 유일하게 구별하지 못한다
3약품코드는 반드시 포함해야 한다
4불필요한 속성이 포함되어 있다
정답 및 해설

처방순번이 진료마다 1부터 매겨지면 (진료번호, 처방순번)만으로 처방 한 건이 유일하게 식별되므로 약품코드까지 넣은 주식별자는 최소성을 어긴다.

약품과의 관계는 비식별 관계로 바꿔 약품코드를 일반 외래키 속성으로 두는 편이 적절하다.

  • 세 속성 조합은 당연히 유일하므로 유일성은 만족한다.
  • 약품코드는 유일 식별에 필요 없으므로 빼야 한다.
  • 진료번호는 진료에서 상속받은 외부식별자다.
문제 6
진료배정(환자번호, 진료과, 담당의사)의 기본키는 (환자번호, 진료과)이다. 의사는 한 진료과에만 속해 담당의사 → 진료과가 성립하고, 환자는 진료과별로 담당의사가 1명이다. 이 릴레이션에 대한 설명으로 옳은 것은?
1키가 아닌 속성 간 이행 종속이 있다
2모든 결정자가 후보키라 BCNF이다
33정규형은 만족하지만 BCNF는 아니다
4부분 종속 때문에 2정규형을 위반한다
정답 및 해설

담당의사는 진료과를 결정하는 결정자이지만 후보키가 아니므로 BCNF 를 위반하고, 종속되는 진료과가 키에 속한 주요 속성이라 3정규형은 만족한다.

후보키는 (환자번호, 진료과)와 (환자번호, 담당의사) 둘이며 모든 속성이 키에 포함되어 부분·이행 종속 대상이 없다.

  • 키가 아닌 일반 속성이 없어 부분 종속으로 2정규형을 깨지 않는다.
  • 이행 종속 대상인 일반 속성이 없어 3정규형은 성립한다.
  • 담당의사가 후보키가 아닌 결정자라 BCNF 가 아니다.
문제 7
의사 엔터티에 지도전문의번호(같은 의사 엔터티의 의사번호를 참조)를 두어 지도 관계를 표현한 모델에 대한 설명으로 옳은 것은?
1최상위 의사는 지도전문의번호가 NULL이다
2지도전문의번호는 진료과의 PK를 참조한다
3조회 시 서로 다른 두 테이블을 조인한다
4지도 관계는 별도 테이블로만 표현된다
정답 및 해설

같은 엔터티를 참조하는 자기참조(순환) 관계에서 지도받지 않는 최상위 의사는 참조할 대상이 없으므로 지도전문의번호가 NULL 이다.

이 모델은 의사 테이블에 별칭을 두 번 붙인 셀프 조인으로 의사와 지도전문의를 함께 조회한다.

  • 한 테이블 안의 외래키만으로 계층을 표현할 수 있다.
  • 조인 대상은 같은 의사 테이블 하나다.
  • 지도전문의번호는 의사 엔터티 자신의 PK 를 참조한다.
문제 8
처방전과 처방상세가 서로 필수(Mandatory)인 1:M 관계로 모델링되어 있을 때 옳은 설명은?
1둘은 각각 별도 트랜잭션으로 처리한다
2처방전만 먼저 커밋하면 모델을 위반한다
3처방상세는 나중에 따로 추가해도 된다
4처방상세는 처방전보다 먼저 커밋한다
정답 및 해설

양쪽이 모두 필수 관계이면 처방상세가 없는 처방전은 존재할 수 없으므로 둘을 하나의 트랜잭션으로 묶어 함께 커밋해야 한다.

처방전만 커밋하는 순간 상세 없는 처방전이 저장되어 모델이 표현한 업무 규칙이 깨진다.

  • 자식인 처방상세만 먼저 커밋하면 부모 없는 자식이 생긴다.
  • 별도 트랜잭션으로 나누면 한쪽만 반영되는 상태가 생긴다.
  • 상세를 나중에 추가하면 그 사이 필수 관계가 깨진다.
문제 9
수납 테이블의 감면액 속성에 대한 설명으로 옳은 것은?
1Oracle은 ' '(공백)도 NULL로 본다
2NULL끼리 = 로 비교하면 참이다
30은 감면이 없다고 확정된 값이다
4NULL은 0으로 바뀌어 저장된다
정답 및 해설

숫자 0 은 감면이 없다는 사실이 확정된 값이고, NULL 은 값이 아직 정해지지 않았거나 알 수 없는 상태를 뜻한다.

NULL 은 어떤 값과도 같지 않아 = 비교 결과가 UNKNOWN 이므로 IS NULL 로 찾아야 한다.

  • NULL 은 0 으로 바뀌어 저장되지 않는다.
  • NULL 끼리 = 로 비교해도 참이 되지 않는다.
  • Oracle 에서 NULL 로 처리되는 것은 빈 문자열이고 공백 한 칸은 문자다.
문제 10
다음 진료예약 테이블은 시퀀스로 부여한 인조식별자 예약ID를 기본키로 쓴다. '의사 한 명은 같은 일시에 한 건만 예약받는다'는 규칙을 어긴 두 행의 예약ID는?

[진료예약]

예약ID환자번호의사번호예약일시
1P01D12026-03-02 09:00
2P02D12026-03-02 09:30
3P03D22026-03-02 09:00
4P04D12026-03-02 09:30
5P01D22026-03-03 10:00
6P02D12026-03-03 10:00
11, 3
22, 4
35, 6
41, 5
정답 및 해설

예약ID 2와 4는 같은 의사 D1 의 같은 일시 03-02 09:30 예약이라 규칙을 어기지만, 예약ID 가 서로 달라 인조식별자 기본키로는 막히지 않는다.

이런 업무 중복은 (의사번호, 예약일시)에 UNIQUE 제약을 따로 걸어야 방지할 수 있다.

  • 1과 3은 같은 일시이지만 의사가 달라 허용된다.
  • 5와 6도 같은 일시이지만 의사가 D2·D1 로 다르다.
  • 1과 5는 같은 환자의 다른 일시 예약이다.
문제 11
병원의 진료·처방 데이터를 저장해 두고, 여러 사용자가 동시에 조회·변경해도 무결성과 장애 복구를 보장하도록 관리해 주는 소프트웨어는?
1스키마(Schema)
2SQL
3데이터베이스
4DBMS
정답 및 해설

데이터의 정의·조작·제어와 동시성 제어·장애 복구를 맡는 소프트웨어는 DBMS(Database Management System)이다.

데이터베이스는 DBMS 가 관리하는 통합·저장된 데이터의 집합 자체이며, Oracle·SQL Server 같은 제품이 DBMS 에 해당한다.

  • 데이터베이스는 관리 대상인 데이터 집합이지 관리 소프트웨어가 아니다.
  • 스키마는 데이터의 구조와 제약을 정의한 명세다.
  • SQL 은 DBMS 에 명령을 내리는 언어일 뿐 소프트웨어가 아니다.
문제 12
Oracle 기준으로 다음 식 중 결과가 NULL이 아닌 것은?
1NULL / 0
2NULL + 10
3NULL || '외래'
40 * NULL
정답 및 해설

Oracle 의 연결 연산자 || 는 NULL 을 빈 문자열처럼 다루므로 NULL || '외래' 의 결과는 '외래' 이다.

반면 산술 연산은 피연산자에 NULL 이 하나라도 있으면 결과가 NULL 이며, 0 을 곱하거나 0 으로 나눠도 오류 없이 NULL 이 된다.

  • NULL 에 10 을 더해도 NULL 이다.
  • 0 에 NULL 을 곱해도 0 이 아니라 NULL 이다.
  • NULL 을 0 으로 나누면 오류가 아니라 NULL 이다.
문제 13
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은?

[처방]

처방번호약품코드투약량횟수일수
1M01232
2M01232
3M01134
4M02125
5M02215
6M03(null)23
SELECT DISTINCT 약품코드 || '(' || 일수 || '일)' AS 표기,
       투약량 * 횟수 + 일수 AS 값
  FROM 처방
 ORDER BY 표기, 값;
1
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M03(3일)(null)
2
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M03(3일)3
3
표기값
M01(2일)10
M01(4일)7
M02(5일)7
M03(3일)(null)
4
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M02(5일)7
M03(3일)(null)
정답 및 해설

DISTINCT 는 SELECT 결과 행 전체를 비교하므로 원본이 다른 처방 4·5도 표기와 값이 모두 M02(5일)·7 로 같아 한 행만 남는다.

값은 곱셈이 먼저 계산되어 M01(2일)은 2×3+2=8 이고, 투약량이 NULL 인 M03 은 결과가 NULL 이다.

  • NULL 이 섞인 산술은 0 으로 바뀌지 않고 NULL 이다.
  • 덧셈을 먼저 하면 10 이 나오지만 연산 우선순위와 맞지 않는다.
  • 계산 결과가 같은 M02 두 행은 DISTINCT 로 합쳐진다.
문제 14
감면액이 NULL이면 감면이 없는 것으로 보고 청구액을 구하려 한다. 다음 결과를 얻기 위해 ( ㉠ )에 들어갈 식으로 옳은 것은?

[수납]

수납번호진료비감면액
1120002000
28000(null)
3150000
450005000
59000(null)
SELECT 수납번호, 진료비 - ( ㉠ ) AS 청구액
  FROM 수납
 ORDER BY 수납번호;

[원하는 결과]

수납번호청구액
110000
28000
315000
40
59000
1COALESCE(감면액, 0)
2SUM(감면액)
3CASE 감면액 WHEN NULL THEN 0 END
4NULLIF(감면액, 0)
정답 및 해설

COALESCE(감면액, 0) 은 감면액이 NULL 이면 0 을 돌려주므로 수납 2·5는 진료비 전액이 청구액이 되고 나머지는 감면액을 뺀 값이 나온다.

NULL 을 그대로 빼면 NULL 전파로 청구액이 NULL 이 되므로 대체값 처리가 필요하다.

  • NULLIF 는 0 을 NULL 로 바꿔 수납 2·3·5 의 청구액이 NULL 이 된다.
  • SUM 은 집계 함수라 GROUP BY 없이 일반 열과 함께 쓰면 오류가 난다.
  • 단순 CASE 의 WHEN NULL 은 어떤 행과도 일치하지 않아 모든 청구액이 NULL 이 된다.
문제 15
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은? (모든 날짜의 시각은 00:00:00이다)

[입원]

입원번호입원일퇴원일
12026-02-262026-03-02
22026-03-102026-03-10
32026-03-15(null)
42026-03-282026-04-03
SELECT 입원번호, TO_CHAR(입원일, 'MM-DD') AS 입원,
       퇴원일 - 입원일 AS 재원일수
  FROM 입원
 ORDER BY 입원번호;
1
입원번호입원재원일수
102-264
203-100
303-15(null)
403-286
2
입원번호입원재원일수
102-266
203-100
303-15(null)
403-286
3
입원번호입원재원일수
102-264
203-101
303-15(null)
403-286
4
입원번호입원재원일수
102-264
203-100
303-150
403-286
정답 및 해설

Oracle 에서 날짜 - 날짜는 두 날짜 사이의 일수를 숫자로 돌려주므로 2026-02-26 부터 03-02 까지는 2월 28일을 거쳐 4, 같은 날은 0 이다.

퇴원일이 NULL 인 입원번호 3 은 연산 결과가 NULL 이고, TO_CHAR 의 MM-DD 서식은 월·일만 문자로 보여 준다.

  • NULL 과의 날짜 연산은 0 이 아니라 NULL 이다.
  • 2026년 2월은 28일까지라 6 이 아니라 4 이다.
  • 같은 날 빼기는 시작일을 포함해 세지 않으므로 0 이다.
문제 16
Oracle에서 다음 DECODE 식과 항상 같은 결과를 내는 CASE 식은?
DECODE(진료구분, 'O', '외래', '기타')

[보기 식]

㉠ CASE 진료구분 WHEN 'O' THEN '외래' ELSE '기타' END
㉡ CASE WHEN 진료구분 = 'O' THEN '기타' ELSE '외래' END
㉢ CASE 진료구분 WHEN 'O' THEN '외래' END
㉣ CASE 진료구분 WHEN '외래' THEN 'O' ELSE '기타' END
1㉣
2㉠
3㉢
4㉡
정답 및 해설

DECODE(열, 비교값, 결과, 기본값)은 열이 비교값과 같으면 결과를, 아니면 기본값을 돌려주므로 단순 CASE 에 ELSE '기타' 를 둔 식과 같다.

진료구분이 'O' 이면 '외래', 그 밖의 값이나 NULL 이면 '기타' 가 나온다.

  • THEN 과 ELSE 결과를 서로 바꾸면 '외래' 와 '기타' 가 뒤집힌다.
  • ELSE 를 빼면 'O' 가 아닌 행은 NULL 이 된다.
  • 비교값과 결과를 맞바꾸면 진료구분이 '외래' 인 행만 'O' 가 된다.
문제 17
진료 테이블 10행 중 진료과가 '내과'인 행이 4행, 진료과가 NULL인 행이 2행, 그 밖의 진료과인 행이 4행이다. 다음 SQL의 결과 행 수는?
SELECT *
  FROM 진료
 WHERE 진료과 <> '내과';
16행
24행
32행
410행
정답 및 해설

진료과가 NULL 인 행은 <> '내과' 비교 결과가 UNKNOWN 이므로 WHERE 를 통과하지 못하고, 그 밖의 진료과 4행만 남는다.

NULL 행까지 포함하려면 OR 진료과 IS NULL 조건을 따로 붙여야 한다.

  • NULL 인 2행만 남는 것이 아니다.
  • NULL 2행은 부정 조건에서도 포함되지 않아 6행이 아니다.
  • WHERE 조건이 있으므로 전체 10행이 나오지 않는다.
문제 18
다음 SQL의 실행 결과로 옳은 것은?

[진료]

진료번호진료과금액응급
1내과8000N
2외과6000N
3외과3000Y
4내과2000Y
5소아과5000N
6소아과4000N
7(null)9000N
SELECT 진료번호, 진료과
  FROM 진료
 WHERE NOT 진료과 = '내과' AND 금액 >= 5000
    OR 응급 = 'Y'
 ORDER BY 진료번호;
1
진료번호진료과
2외과
3외과
4내과
5소아과
2
진료번호진료과
2외과
3외과
4내과
5소아과
7(null)
3
진료번호진료과
2외과
3외과
4내과
5소아과
6소아과
4
진료번호진료과
2외과
3외과
5소아과
정답 및 해설

우선순위가 NOT > AND > OR 이므로 조건은 ((NOT 진료과 = '내과') AND 금액 >= 5000) OR 응급 = 'Y' 로 묶여 2·5는 앞쪽, 3·4는 응급 조건으로 남는다.

진료과가 NULL 인 7은 NOT 을 거쳐도 UNKNOWN 이라 금액이 커도 빠지고, 금액 4000 인 6도 제외된다.

  • NOT 이 AND 전체에 걸린다고 보면 6까지 잘못 포함된다.
  • NULL 을 내과가 아닌 값으로 보면 7이 잘못 포함된다.
  • OR 를 먼저 묶으면 응급인 내과 4가 빠진다.
문제 19
다음 SQL 중 실행 시 오류가 발생하는 것은?

[진료]

진료번호진료과진료비
1내과12000
2외과30000
3내과8000
4안과(null)
5외과15000
㉠ SELECT COUNT(*), MAX(진료비)
     FROM 진료;

㉡ SELECT 진료과, SUM(진료비)
     FROM 진료;

㉢ SELECT '전체' AS 구분, SUM(진료비)
     FROM 진료;

㉣ SELECT SUM(진료비) / COUNT(*)
     FROM 진료
    WHERE 진료비 > 0;
1㉡
2㉢
3㉠
4㉣
정답 및 해설

GROUP BY 가 없으면 테이블 전체가 하나의 그룹이 되어 결과가 1행이므로, 행마다 값이 다른 진료과를 집계 함수 없이 함께 SELECT 하면 오류가 난다.

문자 상수처럼 그룹 안에서 값이 하나로 정해지는 식은 집계 함수와 함께 써도 된다.

  • ㉠은 전체 행 수와 최댓값을 1행으로 돌려준다.
  • ㉢의 '전체' 는 상수라 집계 결과와 함께 출력된다.
  • ㉣은 WHERE 로 거른 행에 대해 집계끼리 나눈 식이라 정상이다.
문제 20
다음 SQL의 실행 결과로 옳은 것은?

[처방]

처방번호의사약품비
1D15000
2D12000
3D14000
4D23000
5D21000
6D36000
7D33500
8D3(null)
9D42500
SELECT 의사, COUNT(*) AS 건수, SUM(약품비) AS 합계
  FROM 처방
 WHERE 약품비 >= 3000
 GROUP BY 의사
HAVING COUNT(*) >= 2
 ORDER BY 의사;
1
의사건수합계
D1311000
D224000
D339500
2
의사건수합계
D129000
D213000
D329500
3
의사건수합계
D129000
D329500
4
의사건수합계
D139000
D339500
정답 및 해설

WHERE 가 그룹화 전에 실행되어 약품비 3000 이상인 행만 남으므로 D1 은 5000·4000, D2 는 3000, D3 는 6000·3500 이 남고 D4 와 NULL 행은 빠진다.

그 뒤 HAVING 이 남은 행 기준 COUNT(*) 2 이상인 그룹만 고르므로 D1(2건, 9000)과 D3(2건, 9500)만 출력된다.

  • COUNT(*) 는 WHERE 에서 걸러진 행을 세지 않는다.
  • WHERE 를 무시하고 전체 행으로 집계한 결과가 아니다.
  • 건수 1인 D2 는 HAVING 에서 제외된다.
문제 21
다음 SQL의 결과 행 수는?

[처방상세]

상세번호약품코드용법수량
1A식후5
2A식후6
3A식전4
4A(null)10
5A(null)(null)
6B식후12
7B(null)(null)
8B(null)(null)
9C식전3
10C식전7
SELECT 약품코드, 용법, COUNT(*) AS 건수
  FROM 처방상세
 GROUP BY 약품코드, 용법
HAVING SUM(수량) >= 10
 ORDER BY 약품코드, 용법;
14
23
35
46
정답 및 해설

그룹은 (A,식후)·(A,식전)·(A,NULL)·(B,식후)·(B,NULL)·(C,식전) 6개이고, HAVING SUM(수량) >= 10 을 만족하는 그룹은 합계 11·10·12·10 인 4개다.

(A,NULL)은 NULL 수량을 빼고 10 이라 남지만, 수량이 모두 NULL 인 (B,NULL)은 SUM 이 NULL 이라 비교가 UNKNOWN 이 되어 빠지고 (A,식전)은 4 라 빠진다.

  • 용법이 NULL 인 행도 하나의 그룹이 되므로 3이 아니다.
  • (B,NULL)의 합계는 0 이 아니라 NULL 이라 5가 아니다.
  • HAVING 으로 걸러지는 그룹이 있어 6이 아니다.
문제 22
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은?

[접수]

접수번호진료과대기번호
1내과3
2(null)1
3외과2
4내과(null)
5내과1
6(null)4
SELECT 접수번호, 진료과, 대기번호
  FROM 접수
 ORDER BY 진료과 NULLS FIRST, 대기번호 DESC;
1
접수번호진료과대기번호
2(null)1
6(null)4
4내과(null)
1내과3
5내과1
3외과2
2
접수번호진료과대기번호
4내과(null)
1내과3
5내과1
3외과2
6(null)4
2(null)1
3
접수번호진료과대기번호
6(null)4
2(null)1
1내과3
5내과1
4내과(null)
3외과2
4
접수번호진료과대기번호
6(null)4
2(null)1
4내과(null)
1내과3
5내과1
3외과2
정답 및 해설

진료과에 NULLS FIRST 를 주었으므로 진료과 NULL 인 6·2가 맨 앞에 오고, 그 안은 대기번호 DESC 라 4인 6이 먼저다.

Oracle 은 NULL 을 가장 큰 값으로 보아 DESC 정렬에서 NULL 이 맨 앞이므로 내과 안에서는 대기번호 NULL 인 4가 3·1보다 앞선다.

  • DESC 에서 NULL 을 맨 뒤로 보내는 것은 SQL Server 방식이다.
  • 진료과 NULL 그룹도 두 번째 키 DESC 로 정렬된다.
  • NULLS FIRST 를 지정했으므로 NULL 진료과가 뒤로 가지 않는다.
문제 23
진료 테이블과 의사 테이블에 모두 의사번호 열이 있다(의사명은 의사, 진료일은 진료에만 있음). 다음 SQL 에 대한 설명으로 옳은 것은?
SELECT 의사번호, 의사명, 진료일
  FROM 진료 J, 의사 D
 WHERE J.의사번호 = D.의사번호;
1열 이름이 모호해 오류가 발생한다
2FROM 절 첫 테이블의 열이 쓰인다
3의사번호 열이 두 번 출력된다
4조인 조건 오른쪽 열이 쓰인다
정답 및 해설

두 테이블에 모두 있는 의사번호를 SELECT 절에서 한정자 없이 쓰면 어느 테이블 열인지 정할 수 없어 모호성 오류가 난다.

조인 조건처럼 J.의사번호 또는 D.의사번호 로 별칭을 붙여야 하며, 한쪽에만 있는 의사명·진료일은 한정자를 생략해도 된다.

  • DBMS 가 FROM 절 첫 테이블의 열을 골라 주는 규칙은 없다.
  • 조인 조건의 오른쪽 열을 대신 쓰는 규칙도 없다.
  • SELECT 목록에 한 번 적은 열은 오류가 없어도 한 번만 출력된다.
문제 24
다음 SQL의 결과 행 수는?

[환자]

환자번호환자명
1한서준
2오다인
3문가람
4배하율

[의사]

의사번호의사명
101권태오
102남시아
103류지안

[진료]

진료번호환자번호의사번호
11101
21102
32(null)
43103
55101
62101
SELECT P.환자명, D.의사명
  FROM 환자 P, 진료 J, 의사 D
 WHERE P.환자번호 = J.환자번호
   AND J.의사번호 = D.의사번호
 ORDER BY J.진료번호;
15
215
36
44
정답 및 해설

세 테이블을 N-1=2개의 등가 조인 조건으로 모두 연결했으므로 환자와 의사 양쪽에 짝이 있는 진료 1·2·4·6의 4행만 남는다.

진료 3은 의사번호가 NULL 이라 의사와 비교가 성립하지 않고, 진료 5는 환자번호 5가 환자 테이블에 없어 빠진다.

  • 의사번호 NULL 행은 등가 조인에서 제외되므로 5가 아니다.
  • 짝이 없는 진료 행이 빠지므로 진료 전체 6이 아니다.
  • 조인 조건이 두 개 모두 있어 카티션 곱 15가 생기지 않는다.
문제 25
3월 진료 기록이 없는 환자를 조회하려 한다. 다음 결과를 얻기 위해 빈칸에 들어갈 구문으로 옳은 것은?

[환자]

환자번호환자명
P1윤채아
P2길도현
P3성예서
P4탁민규
P5현소율

[진료]

진료번호환자번호진료월
1P13
2P14
3P24
4P33
5P5(null)
SELECT P.환자번호, P.환자명
  FROM 환자 P LEFT OUTER JOIN 진료 J
    ON P.환자번호 = J.환자번호
  (   빈칸   )
 ORDER BY P.환자번호;

[빈칸 후보]

㉠ AND J.진료월 = 3 WHERE J.진료번호 IS NULL
㉡ WHERE J.진료월 = 3 AND J.진료번호 IS NULL
㉢ AND J.진료월 = 3 AND J.진료번호 IS NULL
㉣ WHERE J.진료월 = 3 OR J.진료번호 IS NULL

[원하는 결과]

환자번호환자명
P2길도현
P4탁민규
P5현소율
1㉢
2㉠
3㉣
4㉡
정답 및 해설

3월 조건을 ON 절에 두면 3월 진료만 조인되고 나머지 환자는 J 열이 NULL 로 보존되므로, WHERE J.진료번호 IS NULL 로 짝이 없는 P2·P4·P5 만 고를 수 있다.

P2 는 4월 진료만, P5 는 진료월이 NULL 인 진료만 있어 ON 조건을 통과하지 못하고 P4 는 진료가 아예 없다.

  • 월 조건을 WHERE 로 옮기면 IS NULL 과 동시에 참일 수 없어 0행이다.
  • 두 조건을 모두 ON 에 두면 아무 행도 거르지 않아 환자 5명이 다 나온다.
  • WHERE 의 OR 조건은 3월 진료가 있는 P1·P3 와 P4 를 고른다.
문제 26
다음 SQL 의 실행 결과로 옳은 것은?

[진료과]

과코드과명
K1내과
K2외과
K3소아과

[의사]

의사번호의사명과코드
1윤하K1
2서진K1
3도윤K2
4채원K9
SELECT 과코드, 과명, 의사명
  FROM 진료과 FULL OUTER JOIN 의사
 USING (과코드)
 ORDER BY 과코드, 의사명;
1
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
(null)(null)채원
2
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K9(null)채원
3
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
K9(null)채원
4
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
정답 및 해설

USING 으로 조인한 공통 열 과코드는 결과에 한 열로만 나타나며, FULL OUTER JOIN 에서는 어느 쪽 행이든 존재하는 값으로 채워진다.

K1 두 행과 K2 한 행이 짝을 이루고, 의사가 없는 K3 소아과는 의사명이 NULL, 진료과가 없는 채원은 과명이 NULL 이면서 과코드는 K9 로 남는다.

  • 채원 행의 과코드를 NULL 로 본 결과는 왼쪽 테이블 값만 쓴다고 오해한 것이다.
  • K9 행이 빠진 결과는 LEFT OUTER JOIN 에 해당한다.
  • K3 행이 빠진 결과는 RIGHT OUTER JOIN 에 해당한다.
문제 27
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[병동]

병동코드병동명
W1본관3
W2별관5
W3신관7

[입원]

입원번호환자명병동코드퇴원일
1강민W1(null)
2오라W12026-03-04
3남궁솔W2(null)
4표지안W8(null)
5하율(null)(null)
SELECT B.환자명, A.병동명
  FROM 병동 A, 입원 B
 WHERE A.병동코드(+) = B.병동코드
   AND B.퇴원일 IS NULL
 ORDER BY B.입원번호;
1
환자명병동명
강민본관3
남궁솔별관5
(null)신관7
2
환자명병동명
강민본관3
남궁솔별관5
3
환자명병동명
강민본관3
오라본관3
남궁솔별관5
표지안(null)
하율(null)
4
환자명병동명
강민본관3
남궁솔별관5
표지안(null)
하율(null)
정답 및 해설

(+) 는 값이 없을 수 있는 병동 쪽에 붙었으므로 입원 LEFT OUTER JOIN 병동 과 같고, 퇴원일 조건은 기준 테이블 열이라 WHERE 필터로 적용된다.

퇴원한 오라는 걸러지고, 없는 병동코드 W8 인 표지안과 병동코드가 NULL 인 하율은 병동명이 NULL 인 채 남아 4행이 된다.

  • 2행 결과는 짝 없는 입원 행을 버린 INNER JOIN 이다.
  • 신관7 이 나온 결과는 병동을 기준 테이블로 잘못 본 것이다.
  • 오라가 포함된 결과는 퇴원일 조건을 무시한 것이다.
문제 28
다음 SQL 처럼 FROM 절에 쓰여 하나의 테이블처럼 사용되는 서브쿼리를 부르는 이름은?
SELECT P.진료과, P.평균진료비
  FROM (SELECT 진료과, AVG(진료비) AS 평균진료비
          FROM 진료
         GROUP BY 진료과) P
 WHERE P.평균진료비 >= 30000;
1연관 서브쿼리
2다중 열 서브쿼리
3인라인 뷰(Inline View)
4스칼라 서브쿼리
정답 및 해설

FROM 절에 괄호로 넣고 별칭을 붙여 테이블처럼 조회하는 서브쿼리를 인라인 뷰(Inline View)라 한다.

SQL 실행 시점에만 만들어지는 일회성 뷰라 따로 저장되지 않으며, 바깥에서는 별칭 P 와 열 별칭 평균진료비로 참조한다.

  • 스칼라 서브쿼리는 주로 SELECT 절에서 한 행 한 열을 돌려준다.
  • 연관 서브쿼리는 메인쿼리 열을 참조하는데 이 서브쿼리는 그렇지 않다.
  • 다중 열 서브쿼리는 WHERE 절에서 여러 열을 한꺼번에 비교하는 형태다.
문제 29
다음 SQL 의 실행 결과로 옳은 것은?

[의사]

의사번호의사명
1윤하
2서진
3도윤

[진료]

진료번호의사번호진료비
1112000
21(null)
328000
423000
595000
SELECT D.의사명,
       (SELECT COUNT(*) FROM 진료 J
         WHERE J.의사번호 = D.의사번호) AS 건수,
       (SELECT SUM(J.진료비) FROM 진료 J
         WHERE J.의사번호 = D.의사번호) AS 합계
  FROM 의사 D
 ORDER BY D.의사번호;
1
의사명건수합계
윤하212000
서진211000
도윤0(null)
2
의사명건수합계
윤하112000
서진211000
도윤0(null)
3
의사명건수합계
윤하212000
서진211000
4
의사명건수합계
윤하212000
서진211000
도윤00
정답 및 해설

스칼라 서브쿼리는 메인쿼리 의사 행마다 값 하나를 계산하므로 진료가 없는 도윤도 결과에 남고, 대상 행이 없으면 COUNT(*) 는 0, SUM 은 NULL 이 된다.

윤하는 진료비가 NULL 인 행도 COUNT(*) 에 세어 2건이고 SUM 은 NULL 을 제외해 12000이며, 의사번호 9 진료는 어느 의사와도 연결되지 않는다.

  • 도윤의 합계를 0 으로 본 결과는 빈 집합의 SUM 을 0 으로 오해한 것이다.
  • 도윤 행이 빠진 결과는 INNER JOIN 처럼 생각한 것이다.
  • 윤하 건수를 1로 본 결과는 COUNT(*) 가 NULL 행을 뺀다고 오해한 것이다.
문제 30
처방 이력이 없는 환자를 [결과]처럼 조회하려고 한다. ㉠에 들어갈 구문으로 옳은 것은?

[환자]

환자번호환자명
1강민
2오라
3하율
4표지안

[처방]

처방번호환자번호처방일
112026-03-02
212026-03-03
332026-03-03
4(null)2026-03-05
SELECT P.환자명
  FROM 환자 P
 WHERE P.환자번호 NOT IN (SELECT 환자번호
                           FROM 처방
                          ( ㉠ ))
 ORDER BY P.환자명;

[결과]

환자명
오라
표지안
1GROUP BY 환자번호
2WHERE 환자번호 IS NOT NULL
3WHERE 처방번호 > 0
4WHERE 처방일 IS NULL
정답 및 해설

서브쿼리 결과에 NULL 이 섞이면 NOT IN 은 NULL 과의 비교가 UNKNOWN 이 되어 한 행도 남지 않으므로, IS NOT NULL 로 NULL 을 먼저 걸러야 한다.

NULL 을 뺀 서브쿼리 결과는 1, 1, 3 이고, 여기에 없는 오라와 표지안만 조회된다.

  • 처방일이 NULL 인 행은 없어 서브쿼리가 비므로 네 명 모두 조회된다.
  • GROUP BY 는 NULL 도 한 그룹으로 남겨 결과가 0행이 된다.
  • 처방번호 조건은 환자번호 NULL 행을 그대로 남겨 0행이 된다.
문제 31
진료 테이블과 처방 테이블의 결과를 UNION 으로 합칠 때의 작성 방법으로 적절하지 않은 것은?
1각 SELECT 에 WHERE 를 따로 쓸 수 있다
2각 SELECT 에 HAVING 은 쓸 수 없다
3끝의 ORDER BY 에 열 위치 번호를 쓸 수 있다
4열 수가 모자란 쪽은 NULL 로 채울 수 있다
정답 및 해설

집합 연산에 참여하는 각 SELECT 는 독립된 질의라 저마다 WHERE·GROUP BY·HAVING 을 가질 수 있으므로 HAVING 을 쓸 수 없다는 설명은 틀리다.

제약은 열 개수와 대응 열 타입이 맞아야 한다는 점과 ORDER BY 를 맨 끝에 한 번만 쓴다는 점이다.

  • WHERE 는 각 SELECT 안에서 그 SELECT 의 행만 거른다.
  • NULL 이나 상수 열을 넣어 열 개수를 맞추는 것은 흔한 방법이다.
  • 끝의 ORDER BY 에서는 열 이름 대신 위치 번호를 써도 된다.
문제 32
다음 SQL 의 실행 결과로 옳은 것은?

[외래처방]

약품코드용량
A101
A101
B202
C301

[입원처방]

약품코드용량
A102
B202
D401
SELECT 약품코드, 용량 FROM 외래처방
UNION
SELECT 약품코드, 용량 FROM 입원처방
 ORDER BY 약품코드, 용량;
1
약품코드용량
A101
A101
A102
B202
B202
C301
D401
2
약품코드용량
A101
A102
B202
C301
D401
3
약품코드용량
A101
B202
C301
D401
4
약품코드용량
A101
A101
A102
B202
C301
D401
정답 및 해설

UNION 은 두 결과를 합친 뒤 행 전체가 같은 것만 하나로 줄이므로, 약품코드가 같아도 용량이 다른 A10 1 과 A10 2 는 따로 남는다.

외래 안에서 반복된 A10 1 과 양쪽에 모두 있는 B20 2 는 각각 한 행으로 합쳐져 5행이 된다.

  • 4행 결과는 첫 열만 보고 중복을 제거한 것이다.
  • 7행 결과는 중복을 남기는 UNION ALL 의 결과이다.
  • 6행 결과는 한 테이블 안의 중복은 남는다고 오해한 것이다.
문제 33
다음 SQL 이 [결과]를 출력할 때 ㉠에 들어갈 구문으로 옳은 것은?

[진료내역]

진료과유형진료비
내과외래10
내과입원30
외과외래20
외과외래5
SELECT 진료과, 유형, SUM(진료비) AS 합계
  FROM 진료내역
 GROUP BY ( ㉠ )
 ORDER BY 진료과, 유형;

[결과]

진료과유형합계
내과외래10
내과입원30
내과(null)40
외과외래25
외과(null)25
(null)외래35
(null)입원30
(null)(null)65
1진료과, 유형
2CUBE(진료과, 유형)
3GROUPING SETS(진료과, 유형)
4ROLLUP(진료과, 유형)
정답 및 해설

결과에 진료과별 소계와 유형별 소계, 총계가 모두 있으므로 가능한 모든 조합을 만드는 CUBE 이며, 인수가 2개면 2²=4가지 그룹이 생긴다.

상세 3행, 진료과 소계 2행, 유형 소계 2행, 총계 1행을 더해 8행이 된다.

  • ROLLUP(진료과, 유형) 은 유형별 소계가 없어 6행이다.
  • GROUPING SETS 는 각 열별 소계만 만들고 상세와 총계가 없다.
  • 진료과, 유형만 쓰면 소계·총계 없이 상세 3행만 나온다.
문제 34
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[입원현황]

입원번호병동등급
1A일반
2A일반
3A(null)
4B특실
5B일반
SELECT 병동, 등급, COUNT(*) AS 환자수,
       GROUPING(등급) AS G
  FROM 입원현황
 GROUP BY GROUPING SETS ((병동, 등급), (병동))
 ORDER BY 병동, G, 등급;
1
병동등급환자수G
A일반20
A(null)31
B일반10
B특실10
B(null)21
2
병동등급환자수G
A일반20
A(null)10
A(null)31
B일반10
B특실10
B(null)21
(null)(null)51
3
병동등급환자수G
A일반20
A(null)11
A(null)31
B일반10
B특실10
B(null)21
4
병동등급환자수G
A일반20
A(null)10
A(null)31
B일반10
B특실10
B(null)21
정답 및 해설

GROUPING SETS ((병동, 등급), (병동)) 은 병동·등급별 상세와 병동별 소계만 만들고 총계는 만들지 않으며, GROUPING(등급) 은 집계로 생긴 NULL 에만 1 이다.

입원번호 3 의 등급은 원래 NULL 이라 A·NULL·1 은 G 가 0 인 상세 행이고, A·NULL·3 과 B·NULL·2 가 G 가 1 인 소계 행이다.

  • NULL 데이터 행의 G 를 1 로 본 결과는 원래 NULL 과 소계 NULL 을 구분하지 못한 것이다.
  • 총계 행이 있는 결과는 ()가 없는 GROUPING SETS 에 총계를 더한 것이다.
  • A·NULL·1 행이 빠진 결과는 NULL 등급이 그룹에서 빠진다고 오해한 것이다.
문제 35
Oracle 기준으로 진료과별 소계를 얻으려고 작성한 다음 SQL 의 실제 실행 결과로 옳은 것은?

[처방실적]

의사명진료과금액
윤하내과100
윤하내과50
서진내과70
도윤외과40
SELECT 의사명, 진료과, SUM(금액) AS 합계
  FROM 처방실적
 GROUP BY ROLLUP(의사명, 진료과)
 ORDER BY 의사명, 진료과;
1
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
(null)내과220
(null)외과40
(null)(null)260
2
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
(null)(null)260
3
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
4
의사명진료과합계
도윤외과40
서진내과70
윤하내과150
(null)내과220
(null)외과40
(null)(null)260
정답 및 해설

ROLLUP 은 인수 순서대로 계층을 만들어 (의사명, 진료과), (의사명), 총계를 집계하므로 의사명별 소계만 생기고 진료과별 소계는 없다.

의사마다 한 진료과에만 속해 있어 의사별 소계 값이 상세 값과 같고, 마지막에 총계 260 이 붙어 7행이 된다.

  • 내과 220·외과 40 소계가 있는 6행은 인수 순서를 바꾼 ROLLUP(진료과, 의사명) 결과이다.
  • 총계가 없는 결과는 ROLLUP 이 총계도 만든다는 점을 놓친 것이다.
  • 두 방향 소계가 모두 있는 결과는 CUBE 의 결과이다.
문제 36
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[접수]

환자명대기분
강민30
오라(null)
하율30
표지안20
남궁솔10
SELECT 환자명, 대기분,
       RANK() OVER (ORDER BY 대기분 DESC) AS RK,
       DENSE_RANK() OVER (ORDER BY 대기분 DESC) AS DRK
  FROM 접수
 ORDER BY RK, 환자명;
1
환자명대기분RKDRK
오라(null)11
강민3022
하율3032
표지안2043
남궁솔1054
2
환자명대기분RKDRK
오라(null)11
강민3022
하율3022
표지안2033
남궁솔1044
3
환자명대기분RKDRK
오라(null)11
강민3022
하율3022
표지안2043
남궁솔1054
4
환자명대기분RKDRK
강민3011
하율3011
표지안2032
남궁솔1043
오라(null)54
정답 및 해설

Oracle 은 정렬 시 NULL 을 가장 큰 값으로 취급하므로 DESC 순위에서 대기분이 NULL 인 오라가 1위가 된다.

30분 동점 두 명은 둘 다 2위이고, 다음 행은 RANK 가 4위로 건너뛰고 DENSE_RANK 는 3위로 이어진다.

  • 오라가 마지막인 결과는 NULLS LAST 로 정렬한 경우이다.
  • 동점 두 명에 2위와 3위를 준 결과는 ROW_NUMBER 처럼 본 것이다.
  • 표지안 RK 를 3으로 본 결과는 RANK 를 DENSE_RANK 처럼 계산한 것이다.
문제 37
환자별로 투약일 순 누적 용량을 [결과]처럼 구하려고 한다. ㉠에 들어갈 구문으로 옳은 것은?

[투약]

환자번호투약일용량
12026-03-012
12026-03-023
12026-03-041
22026-03-014
22026-03-032
SELECT 환자번호, 투약일, 용량,
       SUM(용량) OVER ( ㉠ ) AS 누적
  FROM 투약
 ORDER BY 환자번호, 투약일;

[결과]

환자번호투약일용량누적
12026-03-0122
12026-03-0235
12026-03-0416
22026-03-0144
22026-03-0326
1ORDER BY 용량
2PARTITION BY 환자번호
3ORDER BY 환자번호, 투약일
4PARTITION BY 환자번호 ORDER BY 투약일
정답 및 해설

환자가 바뀔 때 누적을 다시 시작하려면 PARTITION BY 환자번호, 날짜 순으로 더해 가려면 ORDER BY 투약일 이 함께 있어야 한다.

환자 1 은 2, 5, 6 으로, 환자 2 는 다시 4, 6 으로 누적된다.

  • 파티션 없이 정렬만 하면 환자 2 행이 10, 12 로 이어서 누적된다.
  • PARTITION BY 만 쓰면 모든 행이 환자별 전체 합을 받는다.
  • 용량만으로 정렬하면 환자 구분 없이 용량 순으로 누적되고 같은 용량은 같은 값을 받는다.
문제 38
다음 SQL 의 실행 결과로 옳은 것은?

[혈압측정]

회차수축기
1130
2(null)
3125
4140
SELECT 회차, 수축기,
       LAG(수축기, 1, 0) OVER (ORDER BY 회차) AS 이전,
       LAST_VALUE(수축기) OVER (ORDER BY 회차
         ROWS BETWEEN UNBOUNDED PRECEDING
                  AND UNBOUNDED FOLLOWING) AS 마지막
  FROM 혈압측정
 ORDER BY 회차;
1
회차수축기이전마지막
11300130
2(null)130(null)
3125(null)125
4140125140
2
회차수축기이전마지막
11300140
2(null)130140
3125(null)140
4140125140
3
회차수축기이전마지막
11300140
2(null)130140
31250140
4140125140
4
회차수축기이전마지막
1130(null)140
2(null)130140
3125(null)140
4140125140
정답 및 해설

LAG(수축기, 1, 0) 의 0 은 이전 행이 아예 없는 첫 행에만 쓰이며, 이전 행의 값이 NULL 이면 그대로 NULL 을 가져온다.

프레임을 처음부터 끝까지로 지정한 LAST_VALUE 는 모든 행에서 마지막 회차 값 140 을 돌려준다.

  • 마지막이 각 행 자신의 값인 결과는 기본 프레임(현재 행까지)으로 계산한 것이다.
  • 3회차 이전을 0으로 본 결과는 기본값이 NULL 값도 대체한다고 오해한 것이다.
  • 1회차 이전이 NULL 인 결과는 세 번째 인수를 무시한 것이다.
문제 39
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[약품사용]

약품진료과수량
A내과20
B내과30
C내과(null)
D외과50
SELECT 약품, 진료과, 수량,
       ROUND(RATIO_TO_REPORT(수량)
             OVER (PARTITION BY 진료과), 2) AS 비율
  FROM 약품사용
 ORDER BY 약품;
1
약품진료과수량비율
A내과200.2
B내과300.3
C내과(null)0
D외과500.5
2
약품진료과수량비율
A내과200.2
B내과300.3
C내과(null)(null)
D외과500.5
3
약품진료과수량비율
A내과200.4
B내과300.6
C내과(null)(null)
D외과501
4
약품진료과수량비율
A내과200.4
B내과300.6
C내과(null)0
D외과501
정답 및 해설

RATIO_TO_REPORT 는 값을 파티션 합계로 나눈 비율로, 내과 합계는 NULL 을 뺀 50 이라 A 0.4, B 0.6 이고 외과 D 는 1 이다.

수량이 NULL 인 C 는 나눌 값이 없으므로 비율도 NULL 로 남는다.

  • 0.2·0.3·0.5 인 결과는 PARTITION BY 를 무시하고 전체 합 100 으로 나눈 것이다.
  • C 의 비율을 0 으로 본 결과는 NULL 을 0 으로 바꿔 계산한 것이다.
  • 두 오류를 합친 결과는 전체 합으로 나누고 NULL 도 0 으로 본 것이다.
문제 40
진료비 상위 환자를 조회하려는 다음 SQL 중 실행 시 오류가 발생하는 것은?

[진료]

진료번호과코드환자명진료비
1K1강민30000
2K1오라52000
3K2하율18000
4K2표지안41000

㉠

SELECT 환자명, 진료비
  FROM (SELECT 환자명, 진료비,
               ROW_NUMBER() OVER (ORDER BY 진료비 DESC) AS RN
          FROM 진료) V
 WHERE RN <= 3;

㉡

SELECT 환자명, 진료비,
       ROW_NUMBER() OVER (ORDER BY 진료비 DESC) AS RN
  FROM 진료
 WHERE RN <= 3;

㉢

SELECT 환자명, 진료비
  FROM 진료
 ORDER BY 진료비 DESC
 FETCH FIRST 3 ROWS ONLY;

㉣

SELECT 과코드, 환자명
  FROM (SELECT 과코드, 환자명,
               ROW_NUMBER() OVER (PARTITION BY 과코드
                                  ORDER BY 진료비 DESC) AS RN
          FROM 진료) V
 WHERE RN = 1;
1㉣
2㉢
3㉡
4㉠
정답 및 해설

WHERE 절은 SELECT 절보다 먼저 처리되므로 SELECT 에서 만든 윈도우 함수 별칭 RN 을 같은 쿼리의 WHERE 에서 참조하면 오류가 난다.

순번으로 거르려면 순번을 매긴 쿼리를 인라인 뷰로 감싼 뒤 바깥 WHERE 에서 RN 을 비교해야 한다.

  • ㉠은 인라인 뷰에서 순번을 만든 뒤 바깥에서 거르는 올바른 방식이다.
  • ㉢은 정렬 후 3행만 가져오는 표준 FETCH FIRST 구문이다.
  • ㉣은 과코드별로 순번을 매겨 과마다 최고 진료비 환자를 구한다.
문제 41
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[진료]

진료번호환자명진료비
1강민30000
2오라52000
3하율18000
4표지안41000
5남궁솔30000
6채아9000
SELECT ROWNUM AS RN, 환자명, 진료비
  FROM (SELECT 환자명, 진료비
          FROM 진료
         ORDER BY 진료비 DESC, 진료번호)
 WHERE ROWNUM <= 3;
1
RN환자명진료비
1오라52000
2강민30000
3하율18000
2
RN환자명진료비
1채아9000
2하율18000
3강민30000
3
RN환자명진료비
1오라52000
2표지안41000
3강민30000
4남궁솔30000
4
RN환자명진료비
1오라52000
2표지안41000
3강민30000
정답 및 해설

정렬이 인라인 뷰 안에서 먼저 끝나므로 바깥의 ROWNUM 은 정렬된 순서대로 1, 2, 3 을 붙이고 3행에서 멈춘다.

30000 동점인 강민과 남궁솔은 진료번호 순으로 강민이 앞서므로 강민까지만 포함된다.

  • 강민·하율이 나온 결과는 정렬 전 앞 3행에 ROWNUM 을 매겼다고 본 것이다.
  • 4행 결과는 ROWNUM 이 동점 행까지 포함한다고 오해한 것이다.
  • 진료비가 작은 순 결과는 DESC 를 무시한 것이다.
문제 42
Oracle 기준으로 다음 SQL 을 실행했을 때, 결과 중 내과 행에 대한 설명으로 옳은 것은?

[조직]

조직코드조직명상위코드
H0병원장실(null)
H1진료부H0
H2간호부H0
H3내과H1
H4외과H1
SELECT 조직명, LEVEL AS LV,
       SYS_CONNECT_BY_PATH(조직명, '/') AS 경로,
       CONNECT_BY_ISLEAF AS 말단,
       CONNECT_BY_ROOT 조직명 AS 최상위
  FROM 조직
 START WITH 상위코드 IS NULL
CONNECT BY PRIOR 조직코드 = 상위코드;
1말단 값은 0이다
2LV 값은 2이다
3최상위 값은 진료부이다
4경로 값은 /병원장실/진료부/내과이다
정답 및 해설

SYS_CONNECT_BY_PATH 는 루트부터 현재 행까지 조직명을 구분자로 이어 붙이고 구분자로 시작하므로, 내과의 경로는 /병원장실/진료부/내과이다.

내과는 병원장실, 진료부 다음 세 번째 단계이고 하위 조직이 없어 CONNECT_BY_ISLEAF 가 1 이다.

  • 내과의 LEVEL 은 루트가 1 이므로 3이다.
  • 자식이 없는 행이므로 말단 값은 1이다.
  • CONNECT_BY_ROOT 는 시작 행인 병원장실을 돌려준다.
문제 43
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[의사]

의사ID의사명지도의사ID
1윤하3
2서진1
3도윤2
4채원1
SELECT 의사명, LEVEL AS LV,
       CONNECT_BY_ISCYCLE AS CYC
  FROM 의사
 START WITH 의사ID = 1
CONNECT BY NOCYCLE PRIOR 의사ID = 지도의사ID
 ORDER SIBLINGS BY 의사명;
1
의사명LVCYC
윤하11
서진20
도윤30
채원20
2
의사명LVCYC
윤하10
서진20
도윤31
채원20
3
의사명LVCYC
윤하10
서진20
도윤30
윤하41
채원20
4
의사명LVCYC
윤하10
서진20
채원20
도윤31
정답 및 해설

도윤의 지도 대상이 다시 윤하라 순환이 생기지만 NOCYCLE 이 이미 경로에 있는 윤하로의 전개를 멈추고, 그 순환을 일으킨 도윤 행에 CONNECT_BY_ISCYCLE 1 을 준다.

계층은 깊이 우선으로 전개되어 윤하 아래 서진, 서진 아래 도윤이 먼저 나온 뒤 형제인 채원이 나온다.

  • 윤하에 1 을 준 결과는 순환의 시작점을 표시한다고 오해한 것이다.
  • 채원이 도윤보다 먼저인 결과는 너비 우선으로 전개한 것이다.
  • 윤하가 4레벨로 다시 나온 결과는 NOCYCLE 이 순환 행을 출력한다고 본 것이다.
문제 44
다음 SQL 의 실행 결과로 옳은 것은?

[진료실적]

의사명구분진료비
윤하외래100
윤하외래40
윤하입원300
서진외래50
도윤입원200
SELECT 의사명,
       SUM(CASE WHEN 구분 = '외래' THEN 진료비 ELSE 0 END) AS 외래,
       SUM(CASE WHEN 구분 = '입원' THEN 진료비 END) AS 입원
  FROM 진료실적
 GROUP BY 의사명, 구분
 ORDER BY 의사명, 구분;
1
의사명외래입원
도윤0200
서진50(null)
윤하140(null)
윤하0300
2
의사명외래입원
도윤(null)200
서진50(null)
윤하140(null)
윤하(null)300
3
의사명외래입원
도윤0200
서진50(null)
윤하140300
4
의사명외래입원
도윤0200
서진500
윤하1400
윤하0300
정답 및 해설

GROUP BY 에 피벗 대상 열 구분까지 넣었으므로 의사별 한 행으로 모이지 않고 (의사명, 구분) 조합마다 한 행이 나와 윤하가 두 행으로 나뉜다.

각 행에서 외래 열은 ELSE 0 이라 해당 값이 없으면 0, 입원 열은 ELSE 가 없어 NULL 이 된다.

  • 3행 결과는 GROUP BY 를 의사명만으로 한 올바른 피벗이다.
  • 입원 칸이 0 인 결과는 ELSE 없는 CASE 의 SUM 을 0 으로 본 것이다.
  • 외래 칸이 NULL 인 결과는 ELSE 0 의 효과를 놓친 것이다.
문제 45
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[처방메모]

번호메모
1아침 2정 저녁 1정
21일 3회 10mL
3필요시 복용
4(null)
SELECT 번호,
       REGEXP_COUNT(메모, '[[:digit:]]+') AS 개수,
       REGEXP_REPLACE(메모, '[[:space:]]', '') AS 정리
  FROM 처방메모
 ORDER BY 번호;
1
번호개수정리
12아침2정저녁1정
231일3회10mL
30필요시복용
40(null)
2
번호개수정리
12아침2정저녁1정
241일3회10mL
30필요시복용
4(null)(null)
3
번호개수정리
12아침2정저녁1정
231일3회10mL
30필요시복용
4(null)(null)
4
번호개수정리
12아침2정 저녁 1정
231일3회 10mL
30필요시복용
4(null)(null)
정답 및 해설

[[:digit:]]+ 는 숫자가 한 번 이상 이어진 덩어리를 하나로 세므로 1일 3회 10mL 는 1, 3, 10 으로 3개이고, 숫자가 없는 메모는 0 이다.

Oracle REGEXP_REPLACE 는 기본적으로 일치하는 모든 공백을 지우며, 메모가 NULL 인 행은 두 함수 모두 NULL 을 돌려준다.

  • 첫 공백만 지운 결과는 바꾸기가 한 번만 일어난다고 오해한 것이다.
  • 2번째 메모를 4개로 센 결과는 + 를 무시하고 숫자 한 글자씩 센 것이다.
  • NULL 메모의 개수를 0 으로 본 결과는 NULL 입력이 NULL 을 낸다는 점을 놓친 것이다.
문제 46
다음 MERGE 문을 실행한 뒤 약품재고 테이블의 수량 합계는?

[약품재고]

약품코드수량
A10
B5
C0

[입고]

약품코드수량
B3
D7
E2
MERGE INTO 약품재고 T
USING 입고 S
   ON (T.약품코드 = S.약품코드)
 WHEN MATCHED THEN
   UPDATE SET 수량 = S.수량
 WHEN NOT MATCHED THEN
   INSERT (약품코드, 수량) VALUES (S.약품코드, S.수량);
122
227
324
413
정답 및 해설

B 는 양쪽에 있어 WHEN MATCHED 로 수량이 3 으로 바뀌고, 재고에 없는 D·E 는 WHEN NOT MATCHED 로 새로 들어간다.

입고에 없는 A·C 는 그대로이므로 10+3+0+7+2 로 합계는 22 이다.

  • 13 은 D·E 가 삽입되지 않는다고 본 값이다.
  • 24 는 B 의 수량이 바뀌지 않는다고 본 값이다.
  • 27 은 B 수량을 더해 8 로 만든다고 오해한 값이다.
문제 47
ANSI 표준 격리 수준 기준으로, 다음 T1 에서 두 조회 결과가 달라진 현상은?
T1: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
T1: SELECT COUNT(*) FROM 입원 WHERE 병동 = 'W1';   -- 3
T2: INSERT INTO 입원 VALUES (9, '오라', 'W1');
T2: COMMIT;
T1: SELECT COUNT(*) FROM 입원 WHERE 병동 = 'W1';   -- 4
1Phantom Read
2Non-Repeatable Read
3Dirty Read
4Lost Update
정답 및 해설

같은 조건으로 다시 조회했을 때 다른 트랜잭션이 삽입하고 커밋한 새 행이 나타나 건수가 늘어난 것은 Phantom Read 이다.

ANSI 기준 REPEATABLE READ 는 이미 읽은 행이 바뀌는 것만 막고 새 행의 출현은 허용하며, 이를 막으려면 SERIALIZABLE 이 필요하다.

  • Dirty Read 는 커밋되지 않은 데이터를 읽는 현상이다.
  • Non-Repeatable Read 는 이미 읽은 행의 값이 수정·삭제되어 달라지는 현상이다.
  • Lost Update 는 두 트랜잭션의 갱신 중 하나가 덮여 사라지는 현상이다.
문제 48
Oracle 에서 다음 두 테이블(데이터 없음)에 대해 실행할 때 오류가 발생하는 것은?
CREATE TABLE 진료과 (
  진료과코드 VARCHAR2(5) PRIMARY KEY,
  진료과명   VARCHAR2(20)
);
CREATE TABLE 의사 (
  의사번호   NUMBER PRIMARY KEY,
  의사명     VARCHAR2(20),
  연락처     VARCHAR2(20),
  진료과코드 VARCHAR2(5) REFERENCES 진료과(진료과코드)
);
1TRUNCATE TABLE 의사;
2ALTER TABLE 의사 DROP COLUMN 연락처;
3DROP TABLE 진료과 PURGE;
4ALTER TABLE 의사 RENAME TO 전문의;
정답 및 해설

진료과는 의사 테이블의 외래키(Foreign Key)가 참조하는 부모 테이블이라, 데이터가 없어도 그대로 DROP 하면 오류가 난다.

PURGE 는 휴지통을 거치지 않는 옵션일 뿐이며, 지우려면 CASCADE CONSTRAINTS 로 참조 제약을 함께 없애거나 FK 를 먼저 삭제해야 한다.

  • FK 가 아닌 일반 열 연락처는 DROP COLUMN 으로 삭제된다.
  • RENAME TO 로 자식 테이블 이름을 바꾸는 것은 허용된다.
  • 의사는 참조받지 않는 자식 테이블이라 TRUNCATE 가 된다.
문제 49
환자 테이블에 환자번호 1·2·3 이 있고 진료·처방에 아래 데이터가 있을 때, 다음 DELETE 실행 후 처방 테이블에 남는 행 수는?
CREATE TABLE 진료 (
  진료번호 INT PRIMARY KEY,
  환자번호 INT REFERENCES 환자(환자번호) ON DELETE CASCADE
);
CREATE TABLE 처방 (
  처방번호 INT PRIMARY KEY,
  진료번호 INT REFERENCES 진료(진료번호) ON DELETE CASCADE
);

DELETE FROM 환자 WHERE 환자번호 = 1;

[진료] (진료번호, 환자번호) : (10, 1) (11, 1) (12, 2) (13, 3)

[처방] (처방번호, 진료번호) : (100, 10) (101, 10) (102, 11) (103, 12) (104, 13) (105, 13)

13
25
34
46
정답 및 해설

환자 1 이 삭제되면 ON DELETE CASCADE 로 그 환자의 진료 10·11 이 삭제되고, 처방의 외래키에도 CASCADE 가 있어 연쇄 삭제가 처방까지 이어진다.

진료 10·11 을 참조하던 처방 100·101·102 가 지워져 103·104·105 의 3행만 남는다.

  • 4 는 진료 10 의 처방만 지워진다고 본 값이다.
  • 5 는 처방 한 행만 지워진다고 본 값이다.
  • 6 은 CASCADE 가 한 단계만 적용된다고 오해한 값이다.
문제 50
Oracle 에서 사용자 간호1 이 받은 CREATE TABLE 권한을 다른 사용자에게 다시 부여할 수 있도록 ㉠에 들어갈 옵션은?
GRANT CREATE TABLE TO 간호1 ( ㉠ );
1WITH READ ONLY
2CASCADE
3WITH CHECK OPTION
4WITH ADMIN OPTION
정답 및 해설

CREATE TABLE 은 시스템 권한이므로 받은 사용자가 다시 부여할 수 있게 하려면 WITH ADMIN OPTION 을 붙인다.

특정 테이블에 대한 SELECT·UPDATE 같은 오브젝트 권한의 재부여는 WITH GRANT OPTION 으로 지정한다.

  • CASCADE 는 권한 회수나 객체 삭제 시 연쇄 처리에 쓰는 키워드이다.
  • WITH CHECK OPTION 은 뷰 조건을 벗어나는 변경을 막는 옵션이다.
  • WITH READ ONLY 는 뷰를 읽기 전용으로 만드는 옵션이다.
SQLD SQL 개발자 실전 모의고사모두CBT · moducbt.com
SQL 개발자 기출유형 모의고사 6회
총 50문항 · 객관식 · 정답·해설 포함
문제 1
병원 진료 시스템을 설계할 때 물리적 데이터 모델링 단계에서 주로 수행하는 작업은?
1정규화로 속성을 정제한다
2핵심 엔터티와 관계를 도출한다
3인덱스와 저장 공간을 설계한다
4업무 범위를 추상화해 정의한다
정답 및 해설

물리적 모델링은 특정 DBMS 의 특성을 반영해 테이블·인덱스·파티션·저장 공간처럼 실제 구현에 필요한 구조를 설계하는 단계이다.

개념적 모델링은 핵심 엔터티와 관계를 추상화하고, 논리적 모델링은 키·속성·관계를 정의하며 정규화를 수행한다.

  • 핵심 엔터티와 관계 도출은 개념적 모델링의 작업이다.
  • 정규화로 속성을 정제하는 것은 논리적 모델링의 작업이다.
  • 업무 범위를 추상화해 정의하는 것은 개념적 모델링에 가깝다.
문제 2
다음 진료 ERD를 작성하는 절차와 방법에 대한 설명으로 적절하지 않은 것은?

그림

1관계를 설정한 뒤에 관계명을 기술한다
2관계 참여도는 관계명보다 먼저 정한다
3업무 중심 엔터티를 왼쪽 상단에 둔다
4엔터티 이름은 단수형 업무 용어로 쓴다
정답 및 해설

ERD 는 엔터티 도출·배치 → 관계 설정 → 관계명 기술 → 관계 참여도 기술 → 관계 필수 여부 기술 순서로 작성하므로 참여도는 관계명 다음에 정한다.

배치할 때는 업무의 중심 엔터티를 왼쪽 상단에 두고, 엔터티 이름은 현업 용어의 단수 명사로 붙인다.

  • 중심 엔터티를 왼쪽 상단에 두는 것은 권장 배치 방법이다.
  • 관계를 먼저 연결한 뒤 관계명을 적는 것이 작성 순서에 맞다.
  • 환자·진료처럼 단수형 업무 용어로 이름 붙이는 것은 명명 원칙에 맞다.
문제 3
진료 엔터티의 속성 이름을 정하는 방법으로 가장 적절한 것은?
1'JRYJ'처럼 약어로 짧게 줄여 쓴다
2엔터티마다 '일자'처럼 같은 이름을 쓴다
3'진료를 받은 날짜'처럼 서술형으로 쓴다
4업무 용어 '진료일자'처럼 명사로 쓴다
정답 및 해설

속성 이름은 해당 업무에서 쓰는 용어를 짧은 명사형으로 붙이는 것이 원칙이다.

서술식 이름과 약어는 의미 전달을 흐리고, 같은 이름이 여러 엔터티에 반복되면 혼동되므로 모델 전체에서 가급적 유일한 이름을 쓴다.

  • 서술식 이름은 속성명으로 쓰지 않는다.
  • 뜻을 알기 어려운 약어는 가능한 한 피한다.
  • 모든 엔터티에 같은 이름을 쓰면 의미가 구분되지 않는다.
문제 4
환자와 의사 사이의 M:N 관계를 '진료' 엔터티로 해소했을 때의 설명으로 옳은 것은?
1환자와 진료는 M:N 관계로 남는다
2진료는 환자의 속성으로 흡수된다
3의사와 진료는 1:1 관계가 된다
4진료는 환자·의사 양쪽의 M 쪽이 된다
정답 및 해설

M:N 관계는 교차 엔터티를 두어 1:M 관계 두 개로 바꾸며, 교차 엔터티는 양쪽 부모와의 관계에서 모두 M 쪽(자식)이 된다.

진료는 환자번호와 의사번호를 외래키로 받아 한 환자의 여러 진료, 한 의사의 여러 진료를 각각 표현한다.

  • 해소 후 환자와 진료는 1:M 관계가 된다.
  • 한 의사가 여러 진료를 맡으므로 1:1 이 아니라 1:M 이다.
  • 진료는 자체 속성을 가진 독립 엔터티로 남는다.
문제 5
다음 ERD에서 처방순번은 진료마다 1부터 매기는 일련번호이다. 처방 엔터티의 주식별자에 대한 설명으로 옳은 것은?

그림

1진료번호는 외부식별자가 아니다
2처방을 유일하게 구별하지 못한다
3약품코드는 반드시 포함해야 한다
4불필요한 속성이 포함되어 있다
정답 및 해설

처방순번이 진료마다 1부터 매겨지면 (진료번호, 처방순번)만으로 처방 한 건이 유일하게 식별되므로 약품코드까지 넣은 주식별자는 최소성을 어긴다.

약품과의 관계는 비식별 관계로 바꿔 약품코드를 일반 외래키 속성으로 두는 편이 적절하다.

  • 세 속성 조합은 당연히 유일하므로 유일성은 만족한다.
  • 약품코드는 유일 식별에 필요 없으므로 빼야 한다.
  • 진료번호는 진료에서 상속받은 외부식별자다.
문제 6
진료배정(환자번호, 진료과, 담당의사)의 기본키는 (환자번호, 진료과)이다. 의사는 한 진료과에만 속해 담당의사 → 진료과가 성립하고, 환자는 진료과별로 담당의사가 1명이다. 이 릴레이션에 대한 설명으로 옳은 것은?
1키가 아닌 속성 간 이행 종속이 있다
2모든 결정자가 후보키라 BCNF이다
33정규형은 만족하지만 BCNF는 아니다
4부분 종속 때문에 2정규형을 위반한다
정답 및 해설

담당의사는 진료과를 결정하는 결정자이지만 후보키가 아니므로 BCNF 를 위반하고, 종속되는 진료과가 키에 속한 주요 속성이라 3정규형은 만족한다.

후보키는 (환자번호, 진료과)와 (환자번호, 담당의사) 둘이며 모든 속성이 키에 포함되어 부분·이행 종속 대상이 없다.

  • 키가 아닌 일반 속성이 없어 부분 종속으로 2정규형을 깨지 않는다.
  • 이행 종속 대상인 일반 속성이 없어 3정규형은 성립한다.
  • 담당의사가 후보키가 아닌 결정자라 BCNF 가 아니다.
문제 7
의사 엔터티에 지도전문의번호(같은 의사 엔터티의 의사번호를 참조)를 두어 지도 관계를 표현한 모델에 대한 설명으로 옳은 것은?
1최상위 의사는 지도전문의번호가 NULL이다
2지도전문의번호는 진료과의 PK를 참조한다
3조회 시 서로 다른 두 테이블을 조인한다
4지도 관계는 별도 테이블로만 표현된다
정답 및 해설

같은 엔터티를 참조하는 자기참조(순환) 관계에서 지도받지 않는 최상위 의사는 참조할 대상이 없으므로 지도전문의번호가 NULL 이다.

이 모델은 의사 테이블에 별칭을 두 번 붙인 셀프 조인으로 의사와 지도전문의를 함께 조회한다.

  • 한 테이블 안의 외래키만으로 계층을 표현할 수 있다.
  • 조인 대상은 같은 의사 테이블 하나다.
  • 지도전문의번호는 의사 엔터티 자신의 PK 를 참조한다.
문제 8
처방전과 처방상세가 서로 필수(Mandatory)인 1:M 관계로 모델링되어 있을 때 옳은 설명은?
1둘은 각각 별도 트랜잭션으로 처리한다
2처방전만 먼저 커밋하면 모델을 위반한다
3처방상세는 나중에 따로 추가해도 된다
4처방상세는 처방전보다 먼저 커밋한다
정답 및 해설

양쪽이 모두 필수 관계이면 처방상세가 없는 처방전은 존재할 수 없으므로 둘을 하나의 트랜잭션으로 묶어 함께 커밋해야 한다.

처방전만 커밋하는 순간 상세 없는 처방전이 저장되어 모델이 표현한 업무 규칙이 깨진다.

  • 자식인 처방상세만 먼저 커밋하면 부모 없는 자식이 생긴다.
  • 별도 트랜잭션으로 나누면 한쪽만 반영되는 상태가 생긴다.
  • 상세를 나중에 추가하면 그 사이 필수 관계가 깨진다.
문제 9
수납 테이블의 감면액 속성에 대한 설명으로 옳은 것은?
1Oracle은 ' '(공백)도 NULL로 본다
2NULL끼리 = 로 비교하면 참이다
30은 감면이 없다고 확정된 값이다
4NULL은 0으로 바뀌어 저장된다
정답 및 해설

숫자 0 은 감면이 없다는 사실이 확정된 값이고, NULL 은 값이 아직 정해지지 않았거나 알 수 없는 상태를 뜻한다.

NULL 은 어떤 값과도 같지 않아 = 비교 결과가 UNKNOWN 이므로 IS NULL 로 찾아야 한다.

  • NULL 은 0 으로 바뀌어 저장되지 않는다.
  • NULL 끼리 = 로 비교해도 참이 되지 않는다.
  • Oracle 에서 NULL 로 처리되는 것은 빈 문자열이고 공백 한 칸은 문자다.
문제 10
다음 진료예약 테이블은 시퀀스로 부여한 인조식별자 예약ID를 기본키로 쓴다. '의사 한 명은 같은 일시에 한 건만 예약받는다'는 규칙을 어긴 두 행의 예약ID는?

[진료예약]

예약ID환자번호의사번호예약일시
1P01D12026-03-02 09:00
2P02D12026-03-02 09:30
3P03D22026-03-02 09:00
4P04D12026-03-02 09:30
5P01D22026-03-03 10:00
6P02D12026-03-03 10:00
11, 3
22, 4
35, 6
41, 5
정답 및 해설

예약ID 2와 4는 같은 의사 D1 의 같은 일시 03-02 09:30 예약이라 규칙을 어기지만, 예약ID 가 서로 달라 인조식별자 기본키로는 막히지 않는다.

이런 업무 중복은 (의사번호, 예약일시)에 UNIQUE 제약을 따로 걸어야 방지할 수 있다.

  • 1과 3은 같은 일시이지만 의사가 달라 허용된다.
  • 5와 6도 같은 일시이지만 의사가 D2·D1 로 다르다.
  • 1과 5는 같은 환자의 다른 일시 예약이다.
문제 11
병원의 진료·처방 데이터를 저장해 두고, 여러 사용자가 동시에 조회·변경해도 무결성과 장애 복구를 보장하도록 관리해 주는 소프트웨어는?
1스키마(Schema)
2SQL
3데이터베이스
4DBMS
정답 및 해설

데이터의 정의·조작·제어와 동시성 제어·장애 복구를 맡는 소프트웨어는 DBMS(Database Management System)이다.

데이터베이스는 DBMS 가 관리하는 통합·저장된 데이터의 집합 자체이며, Oracle·SQL Server 같은 제품이 DBMS 에 해당한다.

  • 데이터베이스는 관리 대상인 데이터 집합이지 관리 소프트웨어가 아니다.
  • 스키마는 데이터의 구조와 제약을 정의한 명세다.
  • SQL 은 DBMS 에 명령을 내리는 언어일 뿐 소프트웨어가 아니다.
문제 12
Oracle 기준으로 다음 식 중 결과가 NULL이 아닌 것은?
1NULL / 0
2NULL + 10
3NULL || '외래'
40 * NULL
정답 및 해설

Oracle 의 연결 연산자 || 는 NULL 을 빈 문자열처럼 다루므로 NULL || '외래' 의 결과는 '외래' 이다.

반면 산술 연산은 피연산자에 NULL 이 하나라도 있으면 결과가 NULL 이며, 0 을 곱하거나 0 으로 나눠도 오류 없이 NULL 이 된다.

  • NULL 에 10 을 더해도 NULL 이다.
  • 0 에 NULL 을 곱해도 0 이 아니라 NULL 이다.
  • NULL 을 0 으로 나누면 오류가 아니라 NULL 이다.
문제 13
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은?

[처방]

처방번호약품코드투약량횟수일수
1M01232
2M01232
3M01134
4M02125
5M02215
6M03(null)23
SELECT DISTINCT 약품코드 || '(' || 일수 || '일)' AS 표기,
       투약량 * 횟수 + 일수 AS 값
  FROM 처방
 ORDER BY 표기, 값;
1
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M03(3일)(null)
2
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M03(3일)3
3
표기값
M01(2일)10
M01(4일)7
M02(5일)7
M03(3일)(null)
4
표기값
M01(2일)8
M01(4일)7
M02(5일)7
M02(5일)7
M03(3일)(null)
정답 및 해설

DISTINCT 는 SELECT 결과 행 전체를 비교하므로 원본이 다른 처방 4·5도 표기와 값이 모두 M02(5일)·7 로 같아 한 행만 남는다.

값은 곱셈이 먼저 계산되어 M01(2일)은 2×3+2=8 이고, 투약량이 NULL 인 M03 은 결과가 NULL 이다.

  • NULL 이 섞인 산술은 0 으로 바뀌지 않고 NULL 이다.
  • 덧셈을 먼저 하면 10 이 나오지만 연산 우선순위와 맞지 않는다.
  • 계산 결과가 같은 M02 두 행은 DISTINCT 로 합쳐진다.
문제 14
감면액이 NULL이면 감면이 없는 것으로 보고 청구액을 구하려 한다. 다음 결과를 얻기 위해 ( ㉠ )에 들어갈 식으로 옳은 것은?

[수납]

수납번호진료비감면액
1120002000
28000(null)
3150000
450005000
59000(null)
SELECT 수납번호, 진료비 - ( ㉠ ) AS 청구액
  FROM 수납
 ORDER BY 수납번호;

[원하는 결과]

수납번호청구액
110000
28000
315000
40
59000
1COALESCE(감면액, 0)
2SUM(감면액)
3CASE 감면액 WHEN NULL THEN 0 END
4NULLIF(감면액, 0)
정답 및 해설

COALESCE(감면액, 0) 은 감면액이 NULL 이면 0 을 돌려주므로 수납 2·5는 진료비 전액이 청구액이 되고 나머지는 감면액을 뺀 값이 나온다.

NULL 을 그대로 빼면 NULL 전파로 청구액이 NULL 이 되므로 대체값 처리가 필요하다.

  • NULLIF 는 0 을 NULL 로 바꿔 수납 2·3·5 의 청구액이 NULL 이 된다.
  • SUM 은 집계 함수라 GROUP BY 없이 일반 열과 함께 쓰면 오류가 난다.
  • 단순 CASE 의 WHEN NULL 은 어떤 행과도 일치하지 않아 모든 청구액이 NULL 이 된다.
문제 15
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은? (모든 날짜의 시각은 00:00:00이다)

[입원]

입원번호입원일퇴원일
12026-02-262026-03-02
22026-03-102026-03-10
32026-03-15(null)
42026-03-282026-04-03
SELECT 입원번호, TO_CHAR(입원일, 'MM-DD') AS 입원,
       퇴원일 - 입원일 AS 재원일수
  FROM 입원
 ORDER BY 입원번호;
1
입원번호입원재원일수
102-264
203-100
303-15(null)
403-286
2
입원번호입원재원일수
102-266
203-100
303-15(null)
403-286
3
입원번호입원재원일수
102-264
203-101
303-15(null)
403-286
4
입원번호입원재원일수
102-264
203-100
303-150
403-286
정답 및 해설

Oracle 에서 날짜 - 날짜는 두 날짜 사이의 일수를 숫자로 돌려주므로 2026-02-26 부터 03-02 까지는 2월 28일을 거쳐 4, 같은 날은 0 이다.

퇴원일이 NULL 인 입원번호 3 은 연산 결과가 NULL 이고, TO_CHAR 의 MM-DD 서식은 월·일만 문자로 보여 준다.

  • NULL 과의 날짜 연산은 0 이 아니라 NULL 이다.
  • 2026년 2월은 28일까지라 6 이 아니라 4 이다.
  • 같은 날 빼기는 시작일을 포함해 세지 않으므로 0 이다.
문제 16
Oracle에서 다음 DECODE 식과 항상 같은 결과를 내는 CASE 식은?
DECODE(진료구분, 'O', '외래', '기타')

[보기 식]

㉠ CASE 진료구분 WHEN 'O' THEN '외래' ELSE '기타' END
㉡ CASE WHEN 진료구분 = 'O' THEN '기타' ELSE '외래' END
㉢ CASE 진료구분 WHEN 'O' THEN '외래' END
㉣ CASE 진료구분 WHEN '외래' THEN 'O' ELSE '기타' END
1㉣
2㉠
3㉢
4㉡
정답 및 해설

DECODE(열, 비교값, 결과, 기본값)은 열이 비교값과 같으면 결과를, 아니면 기본값을 돌려주므로 단순 CASE 에 ELSE '기타' 를 둔 식과 같다.

진료구분이 'O' 이면 '외래', 그 밖의 값이나 NULL 이면 '기타' 가 나온다.

  • THEN 과 ELSE 결과를 서로 바꾸면 '외래' 와 '기타' 가 뒤집힌다.
  • ELSE 를 빼면 'O' 가 아닌 행은 NULL 이 된다.
  • 비교값과 결과를 맞바꾸면 진료구분이 '외래' 인 행만 'O' 가 된다.
문제 17
진료 테이블 10행 중 진료과가 '내과'인 행이 4행, 진료과가 NULL인 행이 2행, 그 밖의 진료과인 행이 4행이다. 다음 SQL의 결과 행 수는?
SELECT *
  FROM 진료
 WHERE 진료과 <> '내과';
16행
24행
32행
410행
정답 및 해설

진료과가 NULL 인 행은 <> '내과' 비교 결과가 UNKNOWN 이므로 WHERE 를 통과하지 못하고, 그 밖의 진료과 4행만 남는다.

NULL 행까지 포함하려면 OR 진료과 IS NULL 조건을 따로 붙여야 한다.

  • NULL 인 2행만 남는 것이 아니다.
  • NULL 2행은 부정 조건에서도 포함되지 않아 6행이 아니다.
  • WHERE 조건이 있으므로 전체 10행이 나오지 않는다.
문제 18
다음 SQL의 실행 결과로 옳은 것은?

[진료]

진료번호진료과금액응급
1내과8000N
2외과6000N
3외과3000Y
4내과2000Y
5소아과5000N
6소아과4000N
7(null)9000N
SELECT 진료번호, 진료과
  FROM 진료
 WHERE NOT 진료과 = '내과' AND 금액 >= 5000
    OR 응급 = 'Y'
 ORDER BY 진료번호;
1
진료번호진료과
2외과
3외과
4내과
5소아과
2
진료번호진료과
2외과
3외과
4내과
5소아과
7(null)
3
진료번호진료과
2외과
3외과
4내과
5소아과
6소아과
4
진료번호진료과
2외과
3외과
5소아과
정답 및 해설

우선순위가 NOT > AND > OR 이므로 조건은 ((NOT 진료과 = '내과') AND 금액 >= 5000) OR 응급 = 'Y' 로 묶여 2·5는 앞쪽, 3·4는 응급 조건으로 남는다.

진료과가 NULL 인 7은 NOT 을 거쳐도 UNKNOWN 이라 금액이 커도 빠지고, 금액 4000 인 6도 제외된다.

  • NOT 이 AND 전체에 걸린다고 보면 6까지 잘못 포함된다.
  • NULL 을 내과가 아닌 값으로 보면 7이 잘못 포함된다.
  • OR 를 먼저 묶으면 응급인 내과 4가 빠진다.
문제 19
다음 SQL 중 실행 시 오류가 발생하는 것은?

[진료]

진료번호진료과진료비
1내과12000
2외과30000
3내과8000
4안과(null)
5외과15000
㉠ SELECT COUNT(*), MAX(진료비)
     FROM 진료;

㉡ SELECT 진료과, SUM(진료비)
     FROM 진료;

㉢ SELECT '전체' AS 구분, SUM(진료비)
     FROM 진료;

㉣ SELECT SUM(진료비) / COUNT(*)
     FROM 진료
    WHERE 진료비 > 0;
1㉡
2㉢
3㉠
4㉣
정답 및 해설

GROUP BY 가 없으면 테이블 전체가 하나의 그룹이 되어 결과가 1행이므로, 행마다 값이 다른 진료과를 집계 함수 없이 함께 SELECT 하면 오류가 난다.

문자 상수처럼 그룹 안에서 값이 하나로 정해지는 식은 집계 함수와 함께 써도 된다.

  • ㉠은 전체 행 수와 최댓값을 1행으로 돌려준다.
  • ㉢의 '전체' 는 상수라 집계 결과와 함께 출력된다.
  • ㉣은 WHERE 로 거른 행에 대해 집계끼리 나눈 식이라 정상이다.
문제 20
다음 SQL의 실행 결과로 옳은 것은?

[처방]

처방번호의사약품비
1D15000
2D12000
3D14000
4D23000
5D21000
6D36000
7D33500
8D3(null)
9D42500
SELECT 의사, COUNT(*) AS 건수, SUM(약품비) AS 합계
  FROM 처방
 WHERE 약품비 >= 3000
 GROUP BY 의사
HAVING COUNT(*) >= 2
 ORDER BY 의사;
1
의사건수합계
D1311000
D224000
D339500
2
의사건수합계
D129000
D213000
D329500
3
의사건수합계
D129000
D329500
4
의사건수합계
D139000
D339500
정답 및 해설

WHERE 가 그룹화 전에 실행되어 약품비 3000 이상인 행만 남으므로 D1 은 5000·4000, D2 는 3000, D3 는 6000·3500 이 남고 D4 와 NULL 행은 빠진다.

그 뒤 HAVING 이 남은 행 기준 COUNT(*) 2 이상인 그룹만 고르므로 D1(2건, 9000)과 D3(2건, 9500)만 출력된다.

  • COUNT(*) 는 WHERE 에서 걸러진 행을 세지 않는다.
  • WHERE 를 무시하고 전체 행으로 집계한 결과가 아니다.
  • 건수 1인 D2 는 HAVING 에서 제외된다.
문제 21
다음 SQL의 결과 행 수는?

[처방상세]

상세번호약품코드용법수량
1A식후5
2A식후6
3A식전4
4A(null)10
5A(null)(null)
6B식후12
7B(null)(null)
8B(null)(null)
9C식전3
10C식전7
SELECT 약품코드, 용법, COUNT(*) AS 건수
  FROM 처방상세
 GROUP BY 약품코드, 용법
HAVING SUM(수량) >= 10
 ORDER BY 약품코드, 용법;
14
23
35
46
정답 및 해설

그룹은 (A,식후)·(A,식전)·(A,NULL)·(B,식후)·(B,NULL)·(C,식전) 6개이고, HAVING SUM(수량) >= 10 을 만족하는 그룹은 합계 11·10·12·10 인 4개다.

(A,NULL)은 NULL 수량을 빼고 10 이라 남지만, 수량이 모두 NULL 인 (B,NULL)은 SUM 이 NULL 이라 비교가 UNKNOWN 이 되어 빠지고 (A,식전)은 4 라 빠진다.

  • 용법이 NULL 인 행도 하나의 그룹이 되므로 3이 아니다.
  • (B,NULL)의 합계는 0 이 아니라 NULL 이라 5가 아니다.
  • HAVING 으로 걸러지는 그룹이 있어 6이 아니다.
문제 22
Oracle 기준으로 다음 SQL의 실행 결과로 옳은 것은?

[접수]

접수번호진료과대기번호
1내과3
2(null)1
3외과2
4내과(null)
5내과1
6(null)4
SELECT 접수번호, 진료과, 대기번호
  FROM 접수
 ORDER BY 진료과 NULLS FIRST, 대기번호 DESC;
1
접수번호진료과대기번호
2(null)1
6(null)4
4내과(null)
1내과3
5내과1
3외과2
2
접수번호진료과대기번호
4내과(null)
1내과3
5내과1
3외과2
6(null)4
2(null)1
3
접수번호진료과대기번호
6(null)4
2(null)1
1내과3
5내과1
4내과(null)
3외과2
4
접수번호진료과대기번호
6(null)4
2(null)1
4내과(null)
1내과3
5내과1
3외과2
정답 및 해설

진료과에 NULLS FIRST 를 주었으므로 진료과 NULL 인 6·2가 맨 앞에 오고, 그 안은 대기번호 DESC 라 4인 6이 먼저다.

Oracle 은 NULL 을 가장 큰 값으로 보아 DESC 정렬에서 NULL 이 맨 앞이므로 내과 안에서는 대기번호 NULL 인 4가 3·1보다 앞선다.

  • DESC 에서 NULL 을 맨 뒤로 보내는 것은 SQL Server 방식이다.
  • 진료과 NULL 그룹도 두 번째 키 DESC 로 정렬된다.
  • NULLS FIRST 를 지정했으므로 NULL 진료과가 뒤로 가지 않는다.
문제 23
진료 테이블과 의사 테이블에 모두 의사번호 열이 있다(의사명은 의사, 진료일은 진료에만 있음). 다음 SQL 에 대한 설명으로 옳은 것은?
SELECT 의사번호, 의사명, 진료일
  FROM 진료 J, 의사 D
 WHERE J.의사번호 = D.의사번호;
1열 이름이 모호해 오류가 발생한다
2FROM 절 첫 테이블의 열이 쓰인다
3의사번호 열이 두 번 출력된다
4조인 조건 오른쪽 열이 쓰인다
정답 및 해설

두 테이블에 모두 있는 의사번호를 SELECT 절에서 한정자 없이 쓰면 어느 테이블 열인지 정할 수 없어 모호성 오류가 난다.

조인 조건처럼 J.의사번호 또는 D.의사번호 로 별칭을 붙여야 하며, 한쪽에만 있는 의사명·진료일은 한정자를 생략해도 된다.

  • DBMS 가 FROM 절 첫 테이블의 열을 골라 주는 규칙은 없다.
  • 조인 조건의 오른쪽 열을 대신 쓰는 규칙도 없다.
  • SELECT 목록에 한 번 적은 열은 오류가 없어도 한 번만 출력된다.
문제 24
다음 SQL의 결과 행 수는?

[환자]

환자번호환자명
1한서준
2오다인
3문가람
4배하율

[의사]

의사번호의사명
101권태오
102남시아
103류지안

[진료]

진료번호환자번호의사번호
11101
21102
32(null)
43103
55101
62101
SELECT P.환자명, D.의사명
  FROM 환자 P, 진료 J, 의사 D
 WHERE P.환자번호 = J.환자번호
   AND J.의사번호 = D.의사번호
 ORDER BY J.진료번호;
15
215
36
44
정답 및 해설

세 테이블을 N-1=2개의 등가 조인 조건으로 모두 연결했으므로 환자와 의사 양쪽에 짝이 있는 진료 1·2·4·6의 4행만 남는다.

진료 3은 의사번호가 NULL 이라 의사와 비교가 성립하지 않고, 진료 5는 환자번호 5가 환자 테이블에 없어 빠진다.

  • 의사번호 NULL 행은 등가 조인에서 제외되므로 5가 아니다.
  • 짝이 없는 진료 행이 빠지므로 진료 전체 6이 아니다.
  • 조인 조건이 두 개 모두 있어 카티션 곱 15가 생기지 않는다.
문제 25
3월 진료 기록이 없는 환자를 조회하려 한다. 다음 결과를 얻기 위해 빈칸에 들어갈 구문으로 옳은 것은?

[환자]

환자번호환자명
P1윤채아
P2길도현
P3성예서
P4탁민규
P5현소율

[진료]

진료번호환자번호진료월
1P13
2P14
3P24
4P33
5P5(null)
SELECT P.환자번호, P.환자명
  FROM 환자 P LEFT OUTER JOIN 진료 J
    ON P.환자번호 = J.환자번호
  (   빈칸   )
 ORDER BY P.환자번호;

[빈칸 후보]

㉠ AND J.진료월 = 3 WHERE J.진료번호 IS NULL
㉡ WHERE J.진료월 = 3 AND J.진료번호 IS NULL
㉢ AND J.진료월 = 3 AND J.진료번호 IS NULL
㉣ WHERE J.진료월 = 3 OR J.진료번호 IS NULL

[원하는 결과]

환자번호환자명
P2길도현
P4탁민규
P5현소율
1㉢
2㉠
3㉣
4㉡
정답 및 해설

3월 조건을 ON 절에 두면 3월 진료만 조인되고 나머지 환자는 J 열이 NULL 로 보존되므로, WHERE J.진료번호 IS NULL 로 짝이 없는 P2·P4·P5 만 고를 수 있다.

P2 는 4월 진료만, P5 는 진료월이 NULL 인 진료만 있어 ON 조건을 통과하지 못하고 P4 는 진료가 아예 없다.

  • 월 조건을 WHERE 로 옮기면 IS NULL 과 동시에 참일 수 없어 0행이다.
  • 두 조건을 모두 ON 에 두면 아무 행도 거르지 않아 환자 5명이 다 나온다.
  • WHERE 의 OR 조건은 3월 진료가 있는 P1·P3 와 P4 를 고른다.
문제 26
다음 SQL 의 실행 결과로 옳은 것은?

[진료과]

과코드과명
K1내과
K2외과
K3소아과

[의사]

의사번호의사명과코드
1윤하K1
2서진K1
3도윤K2
4채원K9
SELECT 과코드, 과명, 의사명
  FROM 진료과 FULL OUTER JOIN 의사
 USING (과코드)
 ORDER BY 과코드, 의사명;
1
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
(null)(null)채원
2
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K9(null)채원
3
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
K9(null)채원
4
과코드과명의사명
K1내과서진
K1내과윤하
K2외과도윤
K3소아과(null)
정답 및 해설

USING 으로 조인한 공통 열 과코드는 결과에 한 열로만 나타나며, FULL OUTER JOIN 에서는 어느 쪽 행이든 존재하는 값으로 채워진다.

K1 두 행과 K2 한 행이 짝을 이루고, 의사가 없는 K3 소아과는 의사명이 NULL, 진료과가 없는 채원은 과명이 NULL 이면서 과코드는 K9 로 남는다.

  • 채원 행의 과코드를 NULL 로 본 결과는 왼쪽 테이블 값만 쓴다고 오해한 것이다.
  • K9 행이 빠진 결과는 LEFT OUTER JOIN 에 해당한다.
  • K3 행이 빠진 결과는 RIGHT OUTER JOIN 에 해당한다.
문제 27
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[병동]

병동코드병동명
W1본관3
W2별관5
W3신관7

[입원]

입원번호환자명병동코드퇴원일
1강민W1(null)
2오라W12026-03-04
3남궁솔W2(null)
4표지안W8(null)
5하율(null)(null)
SELECT B.환자명, A.병동명
  FROM 병동 A, 입원 B
 WHERE A.병동코드(+) = B.병동코드
   AND B.퇴원일 IS NULL
 ORDER BY B.입원번호;
1
환자명병동명
강민본관3
남궁솔별관5
(null)신관7
2
환자명병동명
강민본관3
남궁솔별관5
3
환자명병동명
강민본관3
오라본관3
남궁솔별관5
표지안(null)
하율(null)
4
환자명병동명
강민본관3
남궁솔별관5
표지안(null)
하율(null)
정답 및 해설

(+) 는 값이 없을 수 있는 병동 쪽에 붙었으므로 입원 LEFT OUTER JOIN 병동 과 같고, 퇴원일 조건은 기준 테이블 열이라 WHERE 필터로 적용된다.

퇴원한 오라는 걸러지고, 없는 병동코드 W8 인 표지안과 병동코드가 NULL 인 하율은 병동명이 NULL 인 채 남아 4행이 된다.

  • 2행 결과는 짝 없는 입원 행을 버린 INNER JOIN 이다.
  • 신관7 이 나온 결과는 병동을 기준 테이블로 잘못 본 것이다.
  • 오라가 포함된 결과는 퇴원일 조건을 무시한 것이다.
문제 28
다음 SQL 처럼 FROM 절에 쓰여 하나의 테이블처럼 사용되는 서브쿼리를 부르는 이름은?
SELECT P.진료과, P.평균진료비
  FROM (SELECT 진료과, AVG(진료비) AS 평균진료비
          FROM 진료
         GROUP BY 진료과) P
 WHERE P.평균진료비 >= 30000;
1연관 서브쿼리
2다중 열 서브쿼리
3인라인 뷰(Inline View)
4스칼라 서브쿼리
정답 및 해설

FROM 절에 괄호로 넣고 별칭을 붙여 테이블처럼 조회하는 서브쿼리를 인라인 뷰(Inline View)라 한다.

SQL 실행 시점에만 만들어지는 일회성 뷰라 따로 저장되지 않으며, 바깥에서는 별칭 P 와 열 별칭 평균진료비로 참조한다.

  • 스칼라 서브쿼리는 주로 SELECT 절에서 한 행 한 열을 돌려준다.
  • 연관 서브쿼리는 메인쿼리 열을 참조하는데 이 서브쿼리는 그렇지 않다.
  • 다중 열 서브쿼리는 WHERE 절에서 여러 열을 한꺼번에 비교하는 형태다.
문제 29
다음 SQL 의 실행 결과로 옳은 것은?

[의사]

의사번호의사명
1윤하
2서진
3도윤

[진료]

진료번호의사번호진료비
1112000
21(null)
328000
423000
595000
SELECT D.의사명,
       (SELECT COUNT(*) FROM 진료 J
         WHERE J.의사번호 = D.의사번호) AS 건수,
       (SELECT SUM(J.진료비) FROM 진료 J
         WHERE J.의사번호 = D.의사번호) AS 합계
  FROM 의사 D
 ORDER BY D.의사번호;
1
의사명건수합계
윤하212000
서진211000
도윤0(null)
2
의사명건수합계
윤하112000
서진211000
도윤0(null)
3
의사명건수합계
윤하212000
서진211000
4
의사명건수합계
윤하212000
서진211000
도윤00
정답 및 해설

스칼라 서브쿼리는 메인쿼리 의사 행마다 값 하나를 계산하므로 진료가 없는 도윤도 결과에 남고, 대상 행이 없으면 COUNT(*) 는 0, SUM 은 NULL 이 된다.

윤하는 진료비가 NULL 인 행도 COUNT(*) 에 세어 2건이고 SUM 은 NULL 을 제외해 12000이며, 의사번호 9 진료는 어느 의사와도 연결되지 않는다.

  • 도윤의 합계를 0 으로 본 결과는 빈 집합의 SUM 을 0 으로 오해한 것이다.
  • 도윤 행이 빠진 결과는 INNER JOIN 처럼 생각한 것이다.
  • 윤하 건수를 1로 본 결과는 COUNT(*) 가 NULL 행을 뺀다고 오해한 것이다.
문제 30
처방 이력이 없는 환자를 [결과]처럼 조회하려고 한다. ㉠에 들어갈 구문으로 옳은 것은?

[환자]

환자번호환자명
1강민
2오라
3하율
4표지안

[처방]

처방번호환자번호처방일
112026-03-02
212026-03-03
332026-03-03
4(null)2026-03-05
SELECT P.환자명
  FROM 환자 P
 WHERE P.환자번호 NOT IN (SELECT 환자번호
                           FROM 처방
                          ( ㉠ ))
 ORDER BY P.환자명;

[결과]

환자명
오라
표지안
1GROUP BY 환자번호
2WHERE 환자번호 IS NOT NULL
3WHERE 처방번호 > 0
4WHERE 처방일 IS NULL
정답 및 해설

서브쿼리 결과에 NULL 이 섞이면 NOT IN 은 NULL 과의 비교가 UNKNOWN 이 되어 한 행도 남지 않으므로, IS NOT NULL 로 NULL 을 먼저 걸러야 한다.

NULL 을 뺀 서브쿼리 결과는 1, 1, 3 이고, 여기에 없는 오라와 표지안만 조회된다.

  • 처방일이 NULL 인 행은 없어 서브쿼리가 비므로 네 명 모두 조회된다.
  • GROUP BY 는 NULL 도 한 그룹으로 남겨 결과가 0행이 된다.
  • 처방번호 조건은 환자번호 NULL 행을 그대로 남겨 0행이 된다.
문제 31
진료 테이블과 처방 테이블의 결과를 UNION 으로 합칠 때의 작성 방법으로 적절하지 않은 것은?
1각 SELECT 에 WHERE 를 따로 쓸 수 있다
2각 SELECT 에 HAVING 은 쓸 수 없다
3끝의 ORDER BY 에 열 위치 번호를 쓸 수 있다
4열 수가 모자란 쪽은 NULL 로 채울 수 있다
정답 및 해설

집합 연산에 참여하는 각 SELECT 는 독립된 질의라 저마다 WHERE·GROUP BY·HAVING 을 가질 수 있으므로 HAVING 을 쓸 수 없다는 설명은 틀리다.

제약은 열 개수와 대응 열 타입이 맞아야 한다는 점과 ORDER BY 를 맨 끝에 한 번만 쓴다는 점이다.

  • WHERE 는 각 SELECT 안에서 그 SELECT 의 행만 거른다.
  • NULL 이나 상수 열을 넣어 열 개수를 맞추는 것은 흔한 방법이다.
  • 끝의 ORDER BY 에서는 열 이름 대신 위치 번호를 써도 된다.
문제 32
다음 SQL 의 실행 결과로 옳은 것은?

[외래처방]

약품코드용량
A101
A101
B202
C301

[입원처방]

약품코드용량
A102
B202
D401
SELECT 약품코드, 용량 FROM 외래처방
UNION
SELECT 약품코드, 용량 FROM 입원처방
 ORDER BY 약품코드, 용량;
1
약품코드용량
A101
A101
A102
B202
B202
C301
D401
2
약품코드용량
A101
A102
B202
C301
D401
3
약품코드용량
A101
B202
C301
D401
4
약품코드용량
A101
A101
A102
B202
C301
D401
정답 및 해설

UNION 은 두 결과를 합친 뒤 행 전체가 같은 것만 하나로 줄이므로, 약품코드가 같아도 용량이 다른 A10 1 과 A10 2 는 따로 남는다.

외래 안에서 반복된 A10 1 과 양쪽에 모두 있는 B20 2 는 각각 한 행으로 합쳐져 5행이 된다.

  • 4행 결과는 첫 열만 보고 중복을 제거한 것이다.
  • 7행 결과는 중복을 남기는 UNION ALL 의 결과이다.
  • 6행 결과는 한 테이블 안의 중복은 남는다고 오해한 것이다.
문제 33
다음 SQL 이 [결과]를 출력할 때 ㉠에 들어갈 구문으로 옳은 것은?

[진료내역]

진료과유형진료비
내과외래10
내과입원30
외과외래20
외과외래5
SELECT 진료과, 유형, SUM(진료비) AS 합계
  FROM 진료내역
 GROUP BY ( ㉠ )
 ORDER BY 진료과, 유형;

[결과]

진료과유형합계
내과외래10
내과입원30
내과(null)40
외과외래25
외과(null)25
(null)외래35
(null)입원30
(null)(null)65
1진료과, 유형
2CUBE(진료과, 유형)
3GROUPING SETS(진료과, 유형)
4ROLLUP(진료과, 유형)
정답 및 해설

결과에 진료과별 소계와 유형별 소계, 총계가 모두 있으므로 가능한 모든 조합을 만드는 CUBE 이며, 인수가 2개면 2²=4가지 그룹이 생긴다.

상세 3행, 진료과 소계 2행, 유형 소계 2행, 총계 1행을 더해 8행이 된다.

  • ROLLUP(진료과, 유형) 은 유형별 소계가 없어 6행이다.
  • GROUPING SETS 는 각 열별 소계만 만들고 상세와 총계가 없다.
  • 진료과, 유형만 쓰면 소계·총계 없이 상세 3행만 나온다.
문제 34
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[입원현황]

입원번호병동등급
1A일반
2A일반
3A(null)
4B특실
5B일반
SELECT 병동, 등급, COUNT(*) AS 환자수,
       GROUPING(등급) AS G
  FROM 입원현황
 GROUP BY GROUPING SETS ((병동, 등급), (병동))
 ORDER BY 병동, G, 등급;
1
병동등급환자수G
A일반20
A(null)31
B일반10
B특실10
B(null)21
2
병동등급환자수G
A일반20
A(null)10
A(null)31
B일반10
B특실10
B(null)21
(null)(null)51
3
병동등급환자수G
A일반20
A(null)11
A(null)31
B일반10
B특실10
B(null)21
4
병동등급환자수G
A일반20
A(null)10
A(null)31
B일반10
B특실10
B(null)21
정답 및 해설

GROUPING SETS ((병동, 등급), (병동)) 은 병동·등급별 상세와 병동별 소계만 만들고 총계는 만들지 않으며, GROUPING(등급) 은 집계로 생긴 NULL 에만 1 이다.

입원번호 3 의 등급은 원래 NULL 이라 A·NULL·1 은 G 가 0 인 상세 행이고, A·NULL·3 과 B·NULL·2 가 G 가 1 인 소계 행이다.

  • NULL 데이터 행의 G 를 1 로 본 결과는 원래 NULL 과 소계 NULL 을 구분하지 못한 것이다.
  • 총계 행이 있는 결과는 ()가 없는 GROUPING SETS 에 총계를 더한 것이다.
  • A·NULL·1 행이 빠진 결과는 NULL 등급이 그룹에서 빠진다고 오해한 것이다.
문제 35
Oracle 기준으로 진료과별 소계를 얻으려고 작성한 다음 SQL 의 실제 실행 결과로 옳은 것은?

[처방실적]

의사명진료과금액
윤하내과100
윤하내과50
서진내과70
도윤외과40
SELECT 의사명, 진료과, SUM(금액) AS 합계
  FROM 처방실적
 GROUP BY ROLLUP(의사명, 진료과)
 ORDER BY 의사명, 진료과;
1
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
(null)내과220
(null)외과40
(null)(null)260
2
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
(null)(null)260
3
의사명진료과합계
도윤외과40
도윤(null)40
서진내과70
서진(null)70
윤하내과150
윤하(null)150
4
의사명진료과합계
도윤외과40
서진내과70
윤하내과150
(null)내과220
(null)외과40
(null)(null)260
정답 및 해설

ROLLUP 은 인수 순서대로 계층을 만들어 (의사명, 진료과), (의사명), 총계를 집계하므로 의사명별 소계만 생기고 진료과별 소계는 없다.

의사마다 한 진료과에만 속해 있어 의사별 소계 값이 상세 값과 같고, 마지막에 총계 260 이 붙어 7행이 된다.

  • 내과 220·외과 40 소계가 있는 6행은 인수 순서를 바꾼 ROLLUP(진료과, 의사명) 결과이다.
  • 총계가 없는 결과는 ROLLUP 이 총계도 만든다는 점을 놓친 것이다.
  • 두 방향 소계가 모두 있는 결과는 CUBE 의 결과이다.
문제 36
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[접수]

환자명대기분
강민30
오라(null)
하율30
표지안20
남궁솔10
SELECT 환자명, 대기분,
       RANK() OVER (ORDER BY 대기분 DESC) AS RK,
       DENSE_RANK() OVER (ORDER BY 대기분 DESC) AS DRK
  FROM 접수
 ORDER BY RK, 환자명;
1
환자명대기분RKDRK
오라(null)11
강민3022
하율3032
표지안2043
남궁솔1054
2
환자명대기분RKDRK
오라(null)11
강민3022
하율3022
표지안2033
남궁솔1044
3
환자명대기분RKDRK
오라(null)11
강민3022
하율3022
표지안2043
남궁솔1054
4
환자명대기분RKDRK
강민3011
하율3011
표지안2032
남궁솔1043
오라(null)54
정답 및 해설

Oracle 은 정렬 시 NULL 을 가장 큰 값으로 취급하므로 DESC 순위에서 대기분이 NULL 인 오라가 1위가 된다.

30분 동점 두 명은 둘 다 2위이고, 다음 행은 RANK 가 4위로 건너뛰고 DENSE_RANK 는 3위로 이어진다.

  • 오라가 마지막인 결과는 NULLS LAST 로 정렬한 경우이다.
  • 동점 두 명에 2위와 3위를 준 결과는 ROW_NUMBER 처럼 본 것이다.
  • 표지안 RK 를 3으로 본 결과는 RANK 를 DENSE_RANK 처럼 계산한 것이다.
문제 37
환자별로 투약일 순 누적 용량을 [결과]처럼 구하려고 한다. ㉠에 들어갈 구문으로 옳은 것은?

[투약]

환자번호투약일용량
12026-03-012
12026-03-023
12026-03-041
22026-03-014
22026-03-032
SELECT 환자번호, 투약일, 용량,
       SUM(용량) OVER ( ㉠ ) AS 누적
  FROM 투약
 ORDER BY 환자번호, 투약일;

[결과]

환자번호투약일용량누적
12026-03-0122
12026-03-0235
12026-03-0416
22026-03-0144
22026-03-0326
1ORDER BY 용량
2PARTITION BY 환자번호
3ORDER BY 환자번호, 투약일
4PARTITION BY 환자번호 ORDER BY 투약일
정답 및 해설

환자가 바뀔 때 누적을 다시 시작하려면 PARTITION BY 환자번호, 날짜 순으로 더해 가려면 ORDER BY 투약일 이 함께 있어야 한다.

환자 1 은 2, 5, 6 으로, 환자 2 는 다시 4, 6 으로 누적된다.

  • 파티션 없이 정렬만 하면 환자 2 행이 10, 12 로 이어서 누적된다.
  • PARTITION BY 만 쓰면 모든 행이 환자별 전체 합을 받는다.
  • 용량만으로 정렬하면 환자 구분 없이 용량 순으로 누적되고 같은 용량은 같은 값을 받는다.
문제 38
다음 SQL 의 실행 결과로 옳은 것은?

[혈압측정]

회차수축기
1130
2(null)
3125
4140
SELECT 회차, 수축기,
       LAG(수축기, 1, 0) OVER (ORDER BY 회차) AS 이전,
       LAST_VALUE(수축기) OVER (ORDER BY 회차
         ROWS BETWEEN UNBOUNDED PRECEDING
                  AND UNBOUNDED FOLLOWING) AS 마지막
  FROM 혈압측정
 ORDER BY 회차;
1
회차수축기이전마지막
11300130
2(null)130(null)
3125(null)125
4140125140
2
회차수축기이전마지막
11300140
2(null)130140
3125(null)140
4140125140
3
회차수축기이전마지막
11300140
2(null)130140
31250140
4140125140
4
회차수축기이전마지막
1130(null)140
2(null)130140
3125(null)140
4140125140
정답 및 해설

LAG(수축기, 1, 0) 의 0 은 이전 행이 아예 없는 첫 행에만 쓰이며, 이전 행의 값이 NULL 이면 그대로 NULL 을 가져온다.

프레임을 처음부터 끝까지로 지정한 LAST_VALUE 는 모든 행에서 마지막 회차 값 140 을 돌려준다.

  • 마지막이 각 행 자신의 값인 결과는 기본 프레임(현재 행까지)으로 계산한 것이다.
  • 3회차 이전을 0으로 본 결과는 기본값이 NULL 값도 대체한다고 오해한 것이다.
  • 1회차 이전이 NULL 인 결과는 세 번째 인수를 무시한 것이다.
문제 39
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[약품사용]

약품진료과수량
A내과20
B내과30
C내과(null)
D외과50
SELECT 약품, 진료과, 수량,
       ROUND(RATIO_TO_REPORT(수량)
             OVER (PARTITION BY 진료과), 2) AS 비율
  FROM 약품사용
 ORDER BY 약품;
1
약품진료과수량비율
A내과200.2
B내과300.3
C내과(null)0
D외과500.5
2
약품진료과수량비율
A내과200.2
B내과300.3
C내과(null)(null)
D외과500.5
3
약품진료과수량비율
A내과200.4
B내과300.6
C내과(null)(null)
D외과501
4
약품진료과수량비율
A내과200.4
B내과300.6
C내과(null)0
D외과501
정답 및 해설

RATIO_TO_REPORT 는 값을 파티션 합계로 나눈 비율로, 내과 합계는 NULL 을 뺀 50 이라 A 0.4, B 0.6 이고 외과 D 는 1 이다.

수량이 NULL 인 C 는 나눌 값이 없으므로 비율도 NULL 로 남는다.

  • 0.2·0.3·0.5 인 결과는 PARTITION BY 를 무시하고 전체 합 100 으로 나눈 것이다.
  • C 의 비율을 0 으로 본 결과는 NULL 을 0 으로 바꿔 계산한 것이다.
  • 두 오류를 합친 결과는 전체 합으로 나누고 NULL 도 0 으로 본 것이다.
문제 40
진료비 상위 환자를 조회하려는 다음 SQL 중 실행 시 오류가 발생하는 것은?

[진료]

진료번호과코드환자명진료비
1K1강민30000
2K1오라52000
3K2하율18000
4K2표지안41000

㉠

SELECT 환자명, 진료비
  FROM (SELECT 환자명, 진료비,
               ROW_NUMBER() OVER (ORDER BY 진료비 DESC) AS RN
          FROM 진료) V
 WHERE RN <= 3;

㉡

SELECT 환자명, 진료비,
       ROW_NUMBER() OVER (ORDER BY 진료비 DESC) AS RN
  FROM 진료
 WHERE RN <= 3;

㉢

SELECT 환자명, 진료비
  FROM 진료
 ORDER BY 진료비 DESC
 FETCH FIRST 3 ROWS ONLY;

㉣

SELECT 과코드, 환자명
  FROM (SELECT 과코드, 환자명,
               ROW_NUMBER() OVER (PARTITION BY 과코드
                                  ORDER BY 진료비 DESC) AS RN
          FROM 진료) V
 WHERE RN = 1;
1㉣
2㉢
3㉡
4㉠
정답 및 해설

WHERE 절은 SELECT 절보다 먼저 처리되므로 SELECT 에서 만든 윈도우 함수 별칭 RN 을 같은 쿼리의 WHERE 에서 참조하면 오류가 난다.

순번으로 거르려면 순번을 매긴 쿼리를 인라인 뷰로 감싼 뒤 바깥 WHERE 에서 RN 을 비교해야 한다.

  • ㉠은 인라인 뷰에서 순번을 만든 뒤 바깥에서 거르는 올바른 방식이다.
  • ㉢은 정렬 후 3행만 가져오는 표준 FETCH FIRST 구문이다.
  • ㉣은 과코드별로 순번을 매겨 과마다 최고 진료비 환자를 구한다.
문제 41
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[진료]

진료번호환자명진료비
1강민30000
2오라52000
3하율18000
4표지안41000
5남궁솔30000
6채아9000
SELECT ROWNUM AS RN, 환자명, 진료비
  FROM (SELECT 환자명, 진료비
          FROM 진료
         ORDER BY 진료비 DESC, 진료번호)
 WHERE ROWNUM <= 3;
1
RN환자명진료비
1오라52000
2강민30000
3하율18000
2
RN환자명진료비
1채아9000
2하율18000
3강민30000
3
RN환자명진료비
1오라52000
2표지안41000
3강민30000
4남궁솔30000
4
RN환자명진료비
1오라52000
2표지안41000
3강민30000
정답 및 해설

정렬이 인라인 뷰 안에서 먼저 끝나므로 바깥의 ROWNUM 은 정렬된 순서대로 1, 2, 3 을 붙이고 3행에서 멈춘다.

30000 동점인 강민과 남궁솔은 진료번호 순으로 강민이 앞서므로 강민까지만 포함된다.

  • 강민·하율이 나온 결과는 정렬 전 앞 3행에 ROWNUM 을 매겼다고 본 것이다.
  • 4행 결과는 ROWNUM 이 동점 행까지 포함한다고 오해한 것이다.
  • 진료비가 작은 순 결과는 DESC 를 무시한 것이다.
문제 42
Oracle 기준으로 다음 SQL 을 실행했을 때, 결과 중 내과 행에 대한 설명으로 옳은 것은?

[조직]

조직코드조직명상위코드
H0병원장실(null)
H1진료부H0
H2간호부H0
H3내과H1
H4외과H1
SELECT 조직명, LEVEL AS LV,
       SYS_CONNECT_BY_PATH(조직명, '/') AS 경로,
       CONNECT_BY_ISLEAF AS 말단,
       CONNECT_BY_ROOT 조직명 AS 최상위
  FROM 조직
 START WITH 상위코드 IS NULL
CONNECT BY PRIOR 조직코드 = 상위코드;
1말단 값은 0이다
2LV 값은 2이다
3최상위 값은 진료부이다
4경로 값은 /병원장실/진료부/내과이다
정답 및 해설

SYS_CONNECT_BY_PATH 는 루트부터 현재 행까지 조직명을 구분자로 이어 붙이고 구분자로 시작하므로, 내과의 경로는 /병원장실/진료부/내과이다.

내과는 병원장실, 진료부 다음 세 번째 단계이고 하위 조직이 없어 CONNECT_BY_ISLEAF 가 1 이다.

  • 내과의 LEVEL 은 루트가 1 이므로 3이다.
  • 자식이 없는 행이므로 말단 값은 1이다.
  • CONNECT_BY_ROOT 는 시작 행인 병원장실을 돌려준다.
문제 43
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[의사]

의사ID의사명지도의사ID
1윤하3
2서진1
3도윤2
4채원1
SELECT 의사명, LEVEL AS LV,
       CONNECT_BY_ISCYCLE AS CYC
  FROM 의사
 START WITH 의사ID = 1
CONNECT BY NOCYCLE PRIOR 의사ID = 지도의사ID
 ORDER SIBLINGS BY 의사명;
1
의사명LVCYC
윤하11
서진20
도윤30
채원20
2
의사명LVCYC
윤하10
서진20
도윤31
채원20
3
의사명LVCYC
윤하10
서진20
도윤30
윤하41
채원20
4
의사명LVCYC
윤하10
서진20
채원20
도윤31
정답 및 해설

도윤의 지도 대상이 다시 윤하라 순환이 생기지만 NOCYCLE 이 이미 경로에 있는 윤하로의 전개를 멈추고, 그 순환을 일으킨 도윤 행에 CONNECT_BY_ISCYCLE 1 을 준다.

계층은 깊이 우선으로 전개되어 윤하 아래 서진, 서진 아래 도윤이 먼저 나온 뒤 형제인 채원이 나온다.

  • 윤하에 1 을 준 결과는 순환의 시작점을 표시한다고 오해한 것이다.
  • 채원이 도윤보다 먼저인 결과는 너비 우선으로 전개한 것이다.
  • 윤하가 4레벨로 다시 나온 결과는 NOCYCLE 이 순환 행을 출력한다고 본 것이다.
문제 44
다음 SQL 의 실행 결과로 옳은 것은?

[진료실적]

의사명구분진료비
윤하외래100
윤하외래40
윤하입원300
서진외래50
도윤입원200
SELECT 의사명,
       SUM(CASE WHEN 구분 = '외래' THEN 진료비 ELSE 0 END) AS 외래,
       SUM(CASE WHEN 구분 = '입원' THEN 진료비 END) AS 입원
  FROM 진료실적
 GROUP BY 의사명, 구분
 ORDER BY 의사명, 구분;
1
의사명외래입원
도윤0200
서진50(null)
윤하140(null)
윤하0300
2
의사명외래입원
도윤(null)200
서진50(null)
윤하140(null)
윤하(null)300
3
의사명외래입원
도윤0200
서진50(null)
윤하140300
4
의사명외래입원
도윤0200
서진500
윤하1400
윤하0300
정답 및 해설

GROUP BY 에 피벗 대상 열 구분까지 넣었으므로 의사별 한 행으로 모이지 않고 (의사명, 구분) 조합마다 한 행이 나와 윤하가 두 행으로 나뉜다.

각 행에서 외래 열은 ELSE 0 이라 해당 값이 없으면 0, 입원 열은 ELSE 가 없어 NULL 이 된다.

  • 3행 결과는 GROUP BY 를 의사명만으로 한 올바른 피벗이다.
  • 입원 칸이 0 인 결과는 ELSE 없는 CASE 의 SUM 을 0 으로 본 것이다.
  • 외래 칸이 NULL 인 결과는 ELSE 0 의 효과를 놓친 것이다.
문제 45
Oracle 기준으로 다음 SQL 의 실행 결과로 옳은 것은?

[처방메모]

번호메모
1아침 2정 저녁 1정
21일 3회 10mL
3필요시 복용
4(null)
SELECT 번호,
       REGEXP_COUNT(메모, '[[:digit:]]+') AS 개수,
       REGEXP_REPLACE(메모, '[[:space:]]', '') AS 정리
  FROM 처방메모
 ORDER BY 번호;
1
번호개수정리
12아침2정저녁1정
231일3회10mL
30필요시복용
40(null)
2
번호개수정리
12아침2정저녁1정
241일3회10mL
30필요시복용
4(null)(null)
3
번호개수정리
12아침2정저녁1정
231일3회10mL
30필요시복용
4(null)(null)
4
번호개수정리
12아침2정 저녁 1정
231일3회 10mL
30필요시복용
4(null)(null)
정답 및 해설

[[:digit:]]+ 는 숫자가 한 번 이상 이어진 덩어리를 하나로 세므로 1일 3회 10mL 는 1, 3, 10 으로 3개이고, 숫자가 없는 메모는 0 이다.

Oracle REGEXP_REPLACE 는 기본적으로 일치하는 모든 공백을 지우며, 메모가 NULL 인 행은 두 함수 모두 NULL 을 돌려준다.

  • 첫 공백만 지운 결과는 바꾸기가 한 번만 일어난다고 오해한 것이다.
  • 2번째 메모를 4개로 센 결과는 + 를 무시하고 숫자 한 글자씩 센 것이다.
  • NULL 메모의 개수를 0 으로 본 결과는 NULL 입력이 NULL 을 낸다는 점을 놓친 것이다.
문제 46
다음 MERGE 문을 실행한 뒤 약품재고 테이블의 수량 합계는?

[약품재고]

약품코드수량
A10
B5
C0

[입고]

약품코드수량
B3
D7
E2
MERGE INTO 약품재고 T
USING 입고 S
   ON (T.약품코드 = S.약품코드)
 WHEN MATCHED THEN
   UPDATE SET 수량 = S.수량
 WHEN NOT MATCHED THEN
   INSERT (약품코드, 수량) VALUES (S.약품코드, S.수량);
122
227
324
413
정답 및 해설

B 는 양쪽에 있어 WHEN MATCHED 로 수량이 3 으로 바뀌고, 재고에 없는 D·E 는 WHEN NOT MATCHED 로 새로 들어간다.

입고에 없는 A·C 는 그대로이므로 10+3+0+7+2 로 합계는 22 이다.

  • 13 은 D·E 가 삽입되지 않는다고 본 값이다.
  • 24 는 B 의 수량이 바뀌지 않는다고 본 값이다.
  • 27 은 B 수량을 더해 8 로 만든다고 오해한 값이다.
문제 47
ANSI 표준 격리 수준 기준으로, 다음 T1 에서 두 조회 결과가 달라진 현상은?
T1: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
T1: SELECT COUNT(*) FROM 입원 WHERE 병동 = 'W1';   -- 3
T2: INSERT INTO 입원 VALUES (9, '오라', 'W1');
T2: COMMIT;
T1: SELECT COUNT(*) FROM 입원 WHERE 병동 = 'W1';   -- 4
1Phantom Read
2Non-Repeatable Read
3Dirty Read
4Lost Update
정답 및 해설

같은 조건으로 다시 조회했을 때 다른 트랜잭션이 삽입하고 커밋한 새 행이 나타나 건수가 늘어난 것은 Phantom Read 이다.

ANSI 기준 REPEATABLE READ 는 이미 읽은 행이 바뀌는 것만 막고 새 행의 출현은 허용하며, 이를 막으려면 SERIALIZABLE 이 필요하다.

  • Dirty Read 는 커밋되지 않은 데이터를 읽는 현상이다.
  • Non-Repeatable Read 는 이미 읽은 행의 값이 수정·삭제되어 달라지는 현상이다.
  • Lost Update 는 두 트랜잭션의 갱신 중 하나가 덮여 사라지는 현상이다.
문제 48
Oracle 에서 다음 두 테이블(데이터 없음)에 대해 실행할 때 오류가 발생하는 것은?
CREATE TABLE 진료과 (
  진료과코드 VARCHAR2(5) PRIMARY KEY,
  진료과명   VARCHAR2(20)
);
CREATE TABLE 의사 (
  의사번호   NUMBER PRIMARY KEY,
  의사명     VARCHAR2(20),
  연락처     VARCHAR2(20),
  진료과코드 VARCHAR2(5) REFERENCES 진료과(진료과코드)
);
1TRUNCATE TABLE 의사;
2ALTER TABLE 의사 DROP COLUMN 연락처;
3DROP TABLE 진료과 PURGE;
4ALTER TABLE 의사 RENAME TO 전문의;
정답 및 해설

진료과는 의사 테이블의 외래키(Foreign Key)가 참조하는 부모 테이블이라, 데이터가 없어도 그대로 DROP 하면 오류가 난다.

PURGE 는 휴지통을 거치지 않는 옵션일 뿐이며, 지우려면 CASCADE CONSTRAINTS 로 참조 제약을 함께 없애거나 FK 를 먼저 삭제해야 한다.

  • FK 가 아닌 일반 열 연락처는 DROP COLUMN 으로 삭제된다.
  • RENAME TO 로 자식 테이블 이름을 바꾸는 것은 허용된다.
  • 의사는 참조받지 않는 자식 테이블이라 TRUNCATE 가 된다.
문제 49
환자 테이블에 환자번호 1·2·3 이 있고 진료·처방에 아래 데이터가 있을 때, 다음 DELETE 실행 후 처방 테이블에 남는 행 수는?
CREATE TABLE 진료 (
  진료번호 INT PRIMARY KEY,
  환자번호 INT REFERENCES 환자(환자번호) ON DELETE CASCADE
);
CREATE TABLE 처방 (
  처방번호 INT PRIMARY KEY,
  진료번호 INT REFERENCES 진료(진료번호) ON DELETE CASCADE
);

DELETE FROM 환자 WHERE 환자번호 = 1;

[진료] (진료번호, 환자번호) : (10, 1) (11, 1) (12, 2) (13, 3)

[처방] (처방번호, 진료번호) : (100, 10) (101, 10) (102, 11) (103, 12) (104, 13) (105, 13)

13
25
34
46
정답 및 해설

환자 1 이 삭제되면 ON DELETE CASCADE 로 그 환자의 진료 10·11 이 삭제되고, 처방의 외래키에도 CASCADE 가 있어 연쇄 삭제가 처방까지 이어진다.

진료 10·11 을 참조하던 처방 100·101·102 가 지워져 103·104·105 의 3행만 남는다.

  • 4 는 진료 10 의 처방만 지워진다고 본 값이다.
  • 5 는 처방 한 행만 지워진다고 본 값이다.
  • 6 은 CASCADE 가 한 단계만 적용된다고 오해한 값이다.
문제 50
Oracle 에서 사용자 간호1 이 받은 CREATE TABLE 권한을 다른 사용자에게 다시 부여할 수 있도록 ㉠에 들어갈 옵션은?
GRANT CREATE TABLE TO 간호1 ( ㉠ );
1WITH READ ONLY
2CASCADE
3WITH CHECK OPTION
4WITH ADMIN OPTION
정답 및 해설

CREATE TABLE 은 시스템 권한이므로 받은 사용자가 다시 부여할 수 있게 하려면 WITH ADMIN OPTION 을 붙인다.

특정 테이블에 대한 SELECT·UPDATE 같은 오브젝트 권한의 재부여는 WITH GRANT OPTION 으로 지정한다.

  • CASCADE 는 권한 회수나 객체 삭제 시 연쇄 처리에 쓰는 키워드이다.
  • WITH CHECK OPTION 은 뷰 조건을 벗어나는 변경을 막는 옵션이다.
  • WITH READ ONLY 는 뷰를 읽기 전용으로 만드는 옵션이다.