SQLP 모의고사 4회
70문항 · 제한 시간 180분. 다 풀면 과목별 점수와 합격 여부, 문제마다 해설을 보여 줘요.
1. 보험사 차세대 시스템 구축 중 ANSI/SPARC 3단계 스키마 구조와 데이터 독립성에 대해 논의하였다. 다음 중 가장 적절한 것은?
- 보험금청구 테이블을 다른 테이블스페이스로 옮기고 인덱스를 추가해도 개념 스키마가 바뀌지 않는 성질을 논리적 독립성이라 한다.
- 개념 스키마에 '계약' 엔터티의 속성이 추가되어도 기존 청구 조회 화면(외부 스키마)을 수정하지 않아도 되는 성질을 논리적 독립성이라 한다.
- 외부 스키마는 보험사 전체 조직의 데이터를 통합한 하나의 구조로, 모든 사용자 관점을 포괄한다.
- 인덱스 구성, 데이터 파일 배치, 파티션 방식은 개념 스키마에서 정의한다.
2. 다음 업무 설명을 바탕으로 도출한 엔터티를 발생 시점에 따라 분류한 것 중 가장 적절하지 않은 것은?
보험사는 판매할 상품을 등록하고, 설계사는 고객에게 상품을 판매해 계약을 체결한다. 고객은 사고가 나면 계약에 대해 보험금청구를 하고, 심사가 끝나면 보험금 지급 이력이 남는다.
- 상품 - 기본 엔터티: 다른 엔터티와 무관하게 독립적으로 생성되며 자신의 주식별자를 가진다.
- 계약 - 중심 엔터티: 고객·상품 같은 기본 엔터티로부터 발생하고, 청구·지급 같은 행위 엔터티를 많이 파생시킨다.
- 보험금청구 - 행위 엔터티: 두 개 이상의 부모 엔터티로부터 발생하며 업무 진행에 따라 지속적으로 데이터가 쌓인다.
- 고객 - 행위 엔터티: 계약을 체결하는 시점에 비로소 업무상 의미가 생기므로 계약으로부터 파생된다.
3. 계약 엔터티의 속성을 정의하면서 나눈 대화이다. 다음 중 가장 적절한 것은?
- 업무에서 '정상/실효/해지/만기'로 부르던 상태를 관리하기 위해 설계자가 '계약상태코드(01~04)'를 새로 정의했다면, 이는 설계 속성에 해당한다.
- '총납입보험료'처럼 납입 이력에서 계산할 수 있는 파생 속성은 조회 성능을 위해 가능한 한 많이 정의하는 것이 원칙이다.
- 피보험자가 여러 명인 경우를 대비해 '피보험자명' 속성 하나에 '홍길동,김영희'처럼 여러 값을 함께 저장하는 것이 바람직하다.
- '월보험료' 속성의 도메인이란 계약 엔터티에 저장될 수 있는 인스턴스 개수의 범위를 말한다.
4. 다음은 IE(Information Engineering) 표기법으로 그린 관계를 텍스트로 옮긴 것이다. (
||필수 1,o<선택 다수,|<필수 다수, 기호는 상대편 엔터티 쪽 끝에 표시됨) 이 관계를 바르게 해석한 것은?고객 ||──────o< 계약 계약 ||──────|< 담보 계약 ||──────o< 보험금청구- 모든 고객은 반드시 하나 이상의 계약을 가져야 한다.
- 계약은 담보가 하나도 없는 상태로도 존재할 수 있다.
- 보험금청구는 반드시 하나의 계약에 속하며, 청구가 한 건도 없는 계약도 존재할 수 있다.
- 하나의 담보 인스턴스는 여러 계약에 동시에 속할 수 있다.
5. 보험금청구 엔터티에서 아래 후보들은 모두 현재 데이터 기준으로 인스턴스를 유일하게 식별할 수 있다. 주식별자 선정에 대한 설명으로 가장 적절한 것은?
후보 구성 비고 A 청구번호 접수 시 시스템이 즉시 부여 B 청구번호 + 계약번호 C 계약번호 + 청구접수일시 동일 계약에 같은 시각 청구는 업무상 불가 D 사고접수번호 외부 기관이 부여, 접수 며칠 뒤 입력되거나 없는 경우도 있음 - B는 A보다 많은 정보를 담고 있으므로 주식별자로 더 적합하다.
- D는 외부에서 부여한 유일한 번호이므로 주식별자로 가장 적합하다.
- A가 가장 적절하며, B는 최소성, D는 존재성을 만족하지 못한다.
- C는 두 개의 속성으로 구성되므로 후보식별자가 될 수 없다.
6. 다음 [계약담보] 테이블의 정규화 상태와 정규화 결과로 가장 적절한 것은? (반복 그룹은 없고 모든 속성은 원자값이다)
[계약담보] PK: (계약번호, 담보코드) 컬럼: 계약번호, 담보코드, 가입금액, 담보명, 계약일자 함수 종속 (계약번호, 담보코드) → 가입금액 담보코드 → 담보명 계약번호 → 계약일자- 1정규형이다. (계약번호, 담보코드, 가입금액), (담보코드, 담보명), (계약번호, 계약일자)로 분해하면 3정규형을 만족한다.
- 2정규형이다. 이행 종속을 제거하기 위해 담보명을 분리해야 한다.
- 3정규형이다. 다만 결정자가 후보키가 아닌 종속이 있어 BCNF를 위배한다.
- 1정규형이다. (담보코드, 담보명)만 분리하면 2정규형을 만족한다.
7. 보험금 지급 대상은 고객 본인 또는 의료기관(병원 직접 지급) 중 하나다. 이를 아래와 같이 모델링했을 때 설명으로 가장 적절한 것은?
지급 (지급번호 PK, 청구번호 FK, 지급대상구분 -- 'C':고객, 'H':의료기관 고객번호 FK NULL, 의료기관코드 FK NULL, 지급금액) 고객 (고객번호 PK, 고객명) 의료기관 (의료기관코드 PK, 의료기관명)- 지급대상구분 값이 항상 존재하므로 고객번호와 의료기관코드 두 FK 컬럼 모두 NOT NULL 제약을 선언할 수 있다.
- 지급 목록에 지급대상명을 함께 보여 주려면 지급을 고객, 의료기관과 각각 INNER JOIN 하면 모든 지급 건이 조회된다.
- 이처럼 둘 중 하나와만 관계를 맺는 상호배타적(Arc) 관계는 논리 모델에서는 표현할 수 없고 물리 모델에서만 정의할 수 있다.
- 지급대상명을 함께 조회하려면 고객과 의료기관을 각각 OUTER JOIN 한 뒤 지급대상구분에 따라 이름을 선택(CASE 또는 COALESCE)해야 누락이 없다.
8. 다음 관계 정의를 근거로 트랜잭션 범위를 설계할 때 가장 적절한 것은?
고객 ||──o< 계약 (고객은 계약이 없을 수 있음) 계약 ||──|< 담보 (계약은 1건 이상의 담보 필수, 담보는 반드시 계약에 속함) 계약 ||──o< 보험금청구 보험금청구 ||──o< 지급 (심사 중인 청구는 지급이 없음)- 고객과 계약은 관계로 연결되어 있으므로 고객 등록과 첫 계약 등록은 반드시 하나의 트랜잭션으로 처리해야 한다.
- 계약과 담보는 양쪽 모두 필수 관계이므로 함께 입력되어야 하며, 계약만 먼저 COMMIT 하면 담보가 없는 계약이 남을 수 있다.
- 보험금청구와 지급은 관계로 연결되어 있으므로 청구 접수 시점에 지급 행도 같은 트랜잭션에서 입력해야 한다.
- 담보 입력이 실패하면 계약은 COMMIT 해 두고 담보만 다시 입력하는 것이 원자성을 지키는 방법이다.
9. 다음 [계약] 테이블에 SQL을 수행한 결과(A, B, C, D 순)로 옳은 것은?
[계약]
계약번호 월보험료 할인금액 K1 100 10 K2 NULL 20 K3 200 NULL K4 NULL NULL K5 300 30 SELECT AVG(월보험료) AS A, AVG(NVL(월보험료, 0)) AS B, SUM(월보험료 - 할인금액) AS C, SUM(월보험료) - SUM(할인금액) AS D FROM 계약;- 200, 120, 360, 540
- 120, 120, 360, 540
- 200, 120, 540, 540
- 200, 200, 360, 360
10. 보험금청구 엔터티의 PK를 업무 속성 조합 대신 시퀀스로 발번하는 '청구ID'로 정했다. 이에 대한 설명으로 가장 적절한 것은?
- 인조식별자를 PK로 쓰면 업무 조회용 인덱스가 따로 필요 없어 인덱스 수가 줄어든다.
- (계약번호 + 청구순번)은 업무 속성인 계약번호를 포함하므로 본질식별자다.
- 본질식별자는 항상 단일 속성으로 구성되고, 인조식별자는 항상 복합 속성으로 구성된다.
- 같은 청구가 두 번 입력되어도 청구ID가 달라 PK로는 막을 수 없으므로, 본질 속성 조합에 Unique 제약을 별도로 두는 것을 검토해야 한다.
11. 다음 중 Oracle에서 오류가 발생하는 SQL은? ([계약] 테이블에는 계약번호, 상품코드, 계약일자, 월보험료 컬럼이 있다)
- SELECT 계약번호, 월보험료 * 12 AS 연보험료 FROM 계약 ORDER BY 연보험료 DESC;
- SELECT 상품코드, COUNT() FROM 계약 GROUP BY 상품코드 ORDER BY COUNT() DESC;
- SELECT DISTINCT 상품코드 FROM 계약 ORDER BY 계약일자;
- SELECT 계약번호 FROM 계약 ORDER BY 계약일자;
12. 보험 만기일·경과월 계산 로직을 검증하기 위해 다음 SQL을 Oracle에서 수행했다. 결과 A, B, C로 옳은 것은?
SELECT TO_CHAR(ADD_MONTHS(TO_DATE('20240131','YYYYMMDD'), 1), 'YYYYMMDD') AS A, MONTHS_BETWEEN(TO_DATE('20240430','YYYYMMDD'), TO_DATE('20240331','YYYYMMDD')) AS B, TO_CHAR(LAST_DAY(TO_DATE('20240215','YYYYMMDD')), 'DD') AS C FROM DUAL;- 20240229, 0.9677..., 29
- 20240229, 1, 29
- 20240302, 1, 28
- 20240228, 1, 29
13. 다음 [청구] 테이블에 SQL을 수행한 결과로 옳은 것은?
[청구]
청구번호 청구금액 승인금액 지급금액 1 100 80 80 2 200 NULL NULL 3 150 150 NULL 4 NULL NULL NULL SELECT SUM(NVL2(지급금액, 0, COALESCE(승인금액, 청구금액))) AS 미지급예상, COUNT(NULLIF(청구금액, 승인금액)) AS 조정건수 FROM 청구;- 미지급예상 350, 조정건수 3
- 미지급예상 430, 조정건수 2
- 미지급예상 350, 조정건수 1
- 미지급예상 350, 조정건수 2
14. 다음 [계약] 테이블에 SQL을 수행한 결과로 옳은 것은?
[계약]
계약번호 계약상태 월보험료 설계사번호 C1 유지 30000 A1 C2 실효 70000 A2 C3 실효 40000 NULL C4 유지 90000 A1 C5 해지 60000 A3 SELECT COUNT(*) FROM 계약 WHERE 계약상태 = '유지' OR 계약상태 = '실효' AND 월보험료 >= 50000 AND 설계사번호 NOT IN ('A1', NULL);- 2
- 3
- 1
- 0
15. 다음 [보험금청구] 테이블에 SQL을 수행한 결과로 옳은 것은?
[보험금청구]
청구번호 계약번호 청구금액 지급금액 1 C1 100 100 2 C1 200 NULL 3 C2 300 300 4 C2 100 50 5 C3 500 NULL 6 C3 100 NULL SELECT 계약번호, COUNT(지급금액) AS 지급건수, AVG(지급금액) AS 평균지급 FROM 보험금청구 GROUP BY 계약번호 HAVING AVG(NVL(지급금액, 0)) < AVG(청구금액) * 0.8 ORDER BY 계약번호;- C1 / 1 / 50, C3 / 0 / 0
- C1 / 1 / 100, C3 / 0 / NULL
- C1 / 2 / 100, C3 / 2 / NULL
- C1 / 1 / 100, C2 / 2 / 175, C3 / 0 / NULL
16. 다음 [계약] 테이블에 SQL을 Oracle에서 수행했을 때 출력되는 계약번호 순서로 옳은 것은?
[계약]
계약번호 계약상태 해지일자 C1 해지 20240310 C2 유지 NULL C3 실효 NULL C4 해지 20240512 C5 유지 NULL SELECT 계약번호 FROM 계약 ORDER BY CASE WHEN 계약상태 = '유지' THEN 1 ELSE 2 END, 해지일자 DESC, 계약번호 DESC;- C2, C5, C4, C1, C3
- C5, C2, C4, C1, C3
- C5, C2, C3, C4, C1
- C2, C5, C3, C4, C1
17. 다음 두 테이블에 SQL ①, ②를 수행했을 때 결과 건수를 순서대로 나열한 것은?
[계약]
계약번호 C1 C2 C3 [보험금청구]
청구번호 계약번호 처리상태 1 C1 접수 2 C1 지급 3 C2 거절 -- ① SELECT A.계약번호, B.청구번호 FROM 계약 A, 보험금청구 B WHERE A.계약번호 = B.계약번호(+) AND B.처리상태(+) = '지급'; -- ② SELECT A.계약번호, B.청구번호 FROM 계약 A, 보험금청구 B WHERE A.계약번호 = B.계약번호(+) AND B.처리상태 = '지급';- 3, 1
- 4, 1
- 3, 3
- 4, 2
18. 다음 Oracle 조인 SQL과 항상 같은 결과를 반환하는 ANSI 표준 SQL은?
SELECT A.고객번호, B.계약번호 FROM 고객 A, 계약 B WHERE B.고객번호(+) = A.고객번호 AND B.계약상태(+) = '유지' AND A.고객등급 = 'VIP';- SELECT A.고객번호, B.계약번호 FROM 고객 A LEFT OUTER JOIN 계약 B ON B.고객번호 = A.고객번호 WHERE B.계약상태 = '유지' AND A.고객등급 = 'VIP';
- SELECT A.고객번호, B.계약번호 FROM 고객 A LEFT OUTER JOIN 계약 B ON B.고객번호 = A.고객번호 AND B.계약상태 = '유지' AND A.고객등급 = 'VIP';
- SELECT A.고객번호, B.계약번호 FROM 고객 A RIGHT OUTER JOIN 계약 B ON B.고객번호 = A.고객번호 AND B.계약상태 = '유지' WHERE A.고객등급 = 'VIP';
- SELECT A.고객번호, B.계약번호 FROM 고객 A LEFT OUTER JOIN 계약 B ON B.고객번호 = A.고객번호 AND B.계약상태 = '유지' WHERE A.고객등급 = 'VIP';
19. 다음 두 테이블에 SQL ①, ②, ③을 수행한 결과를 순서대로 나열한 것은?
[설계사]
설계사번호 A1 A2 A3 A4 [계약]
계약번호 설계사번호 C1 A1 C2 A1 C3 NULL C4 A3 -- ① SELECT COUNT(*) FROM 설계사 S WHERE S.설계사번호 NOT IN (SELECT 설계사번호 FROM 계약); -- ② SELECT COUNT(*) FROM 설계사 S WHERE NOT EXISTS (SELECT 1 FROM 계약 C WHERE C.설계사번호 = S.설계사번호); -- ③ SELECT COUNT(*) FROM 설계사 S WHERE S.설계사번호 NOT IN (SELECT 설계사번호 FROM 계약 WHERE 설계사번호 IS NOT NULL);- 2, 2, 2
- 0, 2, 2
- 0, 2, 0
- 2, 2, 0
20. 다음 [계약] 테이블에 SQL ①, ②, ③을 수행한 결과를 순서대로 나열한 것은? (상품코드 'P9'인 계약은 없다)
[계약]
계약번호 상품코드 월보험료 C1 P1 30000 C2 P1 50000 C3 P2 40000 C4 P2 70000 C5 P3 50000 -- ① SELECT COUNT(*) FROM 계약 WHERE 월보험료 > ALL (SELECT 월보험료 FROM 계약 WHERE 상품코드 = 'P1'); -- ② SELECT COUNT(*) FROM 계약 WHERE 월보험료 > ANY (SELECT 월보험료 FROM 계약 WHERE 상품코드 = 'P1'); -- ③ SELECT COUNT(*) FROM 계약 WHERE 월보험료 > ALL (SELECT 월보험료 FROM 계약 WHERE 상품코드 = 'P9');- 1, 4, 0
- 1, 3, 5
- 1, 4, 5
- 2, 4, 5
21. 다음 세 테이블에 SQL을 Oracle에서 수행했을 때 결과 행 수는?
테이블 담보코드 값 담보A D01, D01, D02, D03, NULL 담보B D02, D03, D03, D04 제외담보 D03, NULL SELECT 담보코드 FROM 담보A UNION ALL SELECT 담보코드 FROM 담보B MINUS SELECT 담보코드 FROM 제외담보;- 3
- 4
- 7
- 6
22. 다음 [계약] 테이블에 SQL ①, ②, ③을 수행했을 때 결과 행 수를 순서대로 나열한 것은?
[계약]
지역 상품코드 월보험료 서울 P1 30000 서울 P2 50000 서울 P1 20000 부산 P1 40000 부산 P3 60000 대구 P2 70000 -- ① SELECT 지역, 상품코드, SUM(월보험료) FROM 계약 GROUP BY CUBE(지역, 상품코드); -- ② SELECT 지역, 상품코드, SUM(월보험료) FROM 계약 GROUP BY ROLLUP(지역, 상품코드); -- ③ SELECT 지역, 상품코드, SUM(월보험료) FROM 계약 GROUP BY GROUPING SETS ((지역), (상품코드), ());- 12, 9, 6
- 11, 9, 7
- 12, 8, 7
- 12, 9, 7
23. 다음 [지급] 테이블에 SQL을 수행했을 때 지급번호 2번 행의 A, B 값으로 옳은 것은?
[지급]
지급번호 지급일자 지급금액 1 20240301 100 2 20240302 200 3 20240302 50 4 20240305 300 SELECT 지급번호, SUM(지급금액) OVER (ORDER BY 지급일자) AS A, SUM(지급금액) OVER (ORDER BY 지급일자, 지급번호 ROWS UNBOUNDED PRECEDING) AS B FROM 지급;- A = 300, B = 300
- A = 350, B = 350
- A = 350, B = 300
- A = 300, B = 350
24. 다음 [보험금청구] 테이블에 SQL을 수행한 결과로 옳은 것은? (결과는 청구번호 순)
[보험금청구]
청구번호 계약번호 청구일자 청구금액 1 C1 20240101 100 2 C1 20240205 300 3 C1 20240310 200 SELECT 청구번호, LAST_VALUE(청구금액) OVER (PARTITION BY 계약번호 ORDER BY 청구일자) AS LV, LAG(청구금액, 1, 0) OVER (PARTITION BY 계약번호 ORDER BY 청구일자) AS LG FROM 보험금청구 ORDER BY 청구번호;- LV: 200, 200, 200 / LG: 0, 100, 300
- LV: 100, 300, 200 / LG: 0, 100, 300
- LV: 100, 300, 200 / LG: NULL, 100, 300
- LV: 200, 200, 200 / LG: 100, 300, NULL
25. [보험금청구] 테이블의 청구금액이 600, 500, 500, 400, 300인 5건이 있다. 다음 Oracle(12c 이상) SQL 중 결과 건수가 나머지 셋과 다른 것은?
- SELECT * FROM (SELECT 청구번호, 청구금액, DENSE_RANK() OVER (ORDER BY 청구금액 DESC) AS RK FROM 보험금청구) WHERE RK <= 3;
- SELECT 청구번호, 청구금액 FROM 보험금청구 ORDER BY 청구금액 DESC FETCH FIRST 3 ROWS WITH TIES;
- SELECT * FROM (SELECT 청구번호, 청구금액, RANK() OVER (ORDER BY 청구금액 DESC) AS RK FROM 보험금청구) WHERE RK <= 3;
- SELECT * FROM (SELECT 청구번호, 청구금액 FROM 보험금청구 ORDER BY 청구금액 DESC) WHERE ROWNUM <= 3;
26. 다음 [조직] 테이블에 SQL ①, ②를 수행했을 때 결과 건수를 순서대로 나열한 것은?
[조직]
조직코드 상위조직코드 조직명 HQ NULL 본사 S1 HQ 서울지역단 S2 HQ 경기지역단 B1 S1 강남지점 B2 S1 종로지점 B3 S2 수원지점 -- ① SELECT 조직코드, LEVEL FROM 조직 START WITH 상위조직코드 IS NULL CONNECT BY PRIOR 조직코드 = 상위조직코드 AND 조직코드 <> 'S1'; -- ② SELECT 조직코드, LEVEL FROM 조직 WHERE 조직코드 <> 'S1' START WITH 상위조직코드 IS NULL CONNECT BY PRIOR 조직코드 = 상위조직코드;- 5, 5
- 3, 3
- 5, 3
- 3, 5
27. 다음 [지급] 테이블에 SQL ①, ②를 수행했을 때 결과 행 수를 순서대로 나열한 것은?
[지급]
지급번호 계약번호 담보유형 지급금액 1 C1 입원 100 2 C1 수술 200 3 C1 입원 50 4 C2 수술 300 5 C2 입원 400 6 C3 진단 70 -- ① SELECT * FROM (SELECT 계약번호, 담보유형, 지급금액 FROM 지급) PIVOT (SUM(지급금액) FOR 담보유형 IN ('입원' AS 입원, '수술' AS 수술)); -- ② SELECT * FROM 지급 PIVOT (SUM(지급금액) FOR 담보유형 IN ('입원' AS 입원, '수술' AS 수술));- 3, 3
- 3, 6
- 2, 6
- 3, 5
28. [고객등급] 테이블에 다음 MERGE 문을 수행한 뒤의 [고객등급] 내용으로 옳은 것은?
[고객등급] (변경 전)
고객번호 등급 K1 A K2 B K3 C [등급산정] (USING 대상)
고객번호 등급 K1 S K3 D K4 A MERGE INTO 고객등급 T USING 등급산정 S ON (T.고객번호 = S.고객번호) WHEN MATCHED THEN UPDATE SET T.등급 = S.등급 DELETE WHERE T.등급 = 'D' WHEN NOT MATCHED THEN INSERT (고객번호, 등급) VALUES (S.고객번호, S.등급) WHERE S.등급 <> 'A';- (K1, S), (K2, B), (K3, D)
- (K1, S), (K2, B), (K4, A)
- (K1, S), (K2, B)
- (K1, S), (K2, B), (K3, D), (K4, A)
29. 비어 있는 [청구임시] 테이블에 Oracle에서 다음 문장을 순서대로 수행했다. 최종적으로 [청구임시]에 남는 데이터는?
INSERT INTO 청구임시 VALUES (1); SAVEPOINT SP1; INSERT INTO 청구임시 VALUES (2); SAVEPOINT SP2; DELETE FROM 청구임시 WHERE 청구번호 = 1; ROLLBACK TO SP1; INSERT INTO 청구임시 VALUES (3); CREATE TABLE 작업로그 (작업일시 DATE); INSERT INTO 청구임시 VALUES (4); ROLLBACK;- 2건 (1, 3)
- 1건 (1)
- 0건
- 3건 (1, 3, 4)
30. 다음 DDL로 생성한 [보험금청구] 테이블에 대한 설명으로 옳은 것은? ([계약] 테이블은 계약번호가 PK이다)
CREATE TABLE 보험금청구 ( 청구번호 NUMBER CONSTRAINT 청구_PK PRIMARY KEY, 계약번호 VARCHAR2(10) NOT NULL, 청구금액 NUMBER DEFAULT 0 CHECK (청구금액 >= 0), 처리상태 VARCHAR2(2), CONSTRAINT 청구_FK1 FOREIGN KEY (계약번호) REFERENCES 계약 (계약번호) ON DELETE SET NULL );- INSERT 시 청구금액에 명시적으로 NULL을 입력하면 DEFAULT 값 0이 대신 저장된다.
- 청구금액에 NULL을 입력하면 CHECK 제약 위반으로 오류가 발생한다.
- 청구 건이 있는 계약 행을 삭제하면 해당 청구 행도 함께 삭제된다.
- 청구 건이 있는 계약 행을 삭제하려 하면 FK 컬럼을 NULL로 바꿔야 하는데 계약번호가 NOT NULL이므로 삭제가 실패한다.
31. 보험금 지급 처리 중 Oracle 내부 동작에 대한 설명이다. 옳은 것을 모두 고른 것은?
ㄱ. 지급 UPDATE 후 COMMIT 하면 DBWn이 변경된 데이터 블록을 데이터 파일에 기록해야 커밋이 완료된다. ㄴ. 커밋이 완료된 직후 인스턴스가 비정상 종료되어도, 재기동 시 리두 로그로 Roll Forward 하여 커밋된 변경을 복구할 수 있다. ㄷ. 다른 세션이 아직 커밋되지 않은 지급 행을 조회하면, Undo를 이용해 변경 전 이미지(CR 블록)를 만들어 읽는다. ㄹ. 하나의 익스텐트는 테이블스페이스를 구성하는 여러 데이터 파일에 걸쳐 할당될 수 있다.
- ㄱ, ㄴ
- ㄴ, ㄷ
- ㄷ, ㄹ
- ㄴ, ㄷ, ㄹ
32. Oracle 전용 서버(Dedicated Server) 환경에서 다음 중 SGA가 아닌 PGA에 저장되거나 PGA에서 수행되는 것만 고른 것은?
ㄱ. 공유 커서와 실행계획 (라이브러리 캐시) ㄴ. 해시 조인의 Build Input으로 만든 해시 테이블 ㄷ. 소트 머지 조인을 위한 정렬 작업 ㄹ. 데이터 변경 내역을 담는 리두 로그 버퍼 ㅁ. 세션의 바인드 변수 값과 커서 상태 정보
- ㄱ, ㄴ, ㄷ
- ㄴ, ㄷ, ㄹ
- ㄴ, ㄷ, ㅁ
- ㄷ, ㄹ, ㅁ
33. 같은 스키마 사용자가 다음 다섯 SQL을 ①부터 순서대로 처음 실행했다. 라이브러리 캐시에서 발생하는 하드 파싱은 모두 몇 번인가? (옵티마이저 환경은 모두 같다)
① SELECT * FROM 계약 WHERE 계약번호 = :B1 ② select * from 계약 where 계약번호 = :B1 ③ SELECT * FROM 계약 WHERE 계약번호 = :B2 ④ SELECT * FROM 계약 WHERE 계약번호 = :B1 -- 다른 세션에서 실행 ⑤ SELECT * FROM 계약 WHERE 계약번호 = :B1 -- FROM 계약 뒤 공백 2칸- 1번
- 2번
- 3번
- 4번
34. [보험금청구] 테이블의 처리상태 값 분포는 '완료' 98%, '심사중' 1.5%, '보류' 0.5%이며 히스토그램이 수집되어 있다. 온라인 화면에서
WHERE 처리상태 = :st조건의 SQL을 초당 수백 번 실행한다. 다음 설명 중 가장 적절한 것은?- 바인드 변수를 쓰면 하드 파싱 시점에 처음 들어온 값을 엿보고(Peeking) 만든 계획을 이후 실행에서도 재사용하므로, 첫 값이 무엇이었는지에 따라 다른 값의 성능이 크게 나빠질 수 있다.
- 바인드 변수를 쓰면 옵티마이저는 매 실행마다 바인드 값과 히스토그램을 이용해 실행계획을 새로 만든다.
- 11g의 적응적 커서 공유(ACS)는 첫 실행부터 값의 선택도를 판단하여 값마다 다른 실행계획을 즉시 사용한다.
- 처리상태를 리터럴 상수로 바꾸면 커서 공유율이 높아져 하드 파싱이 줄어든다.
35. 다음은 보험금 지급 내역 조회 SQL의 TKPROF 결과이다. 해석으로 가장 적절한 것은?
call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 11 0.92 6.84 7200 11500 500 150 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 13 0.92 6.84 7200 11500 500 150- 논리적 I/O는 11,500블록이고 버퍼캐시 히트율은 약 37%이다.
- 논리적 I/O는 12,000블록이고 버퍼캐시 히트율은 60%이다.
- 논리적 I/O는 12,000블록, 버퍼캐시 히트율은 40%이며, 전체 수행 시간 중 약 5.9초는 CPU가 아닌 대기 시간으로 대부분 디스크 I/O 대기로 추정된다.
- 논리적 I/O는 19,200블록이고 버퍼캐시 히트율은 37.5%이다.
36. [보험금청구] 테이블 세그먼트는 블록 수가 각각 40, 40, 20개인 익스텐트 3개로 구성되어 있고,
db_file_multiblock_read_count = 16이다. 버퍼 캐시에 이 테이블 블록이 하나도 없을 때 Table Full Scan을 수행하면 발생하는 Multiblock I/O Call 횟수와 그 대기 이벤트로 옳은 것은? (세그먼트 헤더 읽기는 무시한다)- 7회, db file sequential read
- 8회, db file scattered read
- 7회, db file scattered read
- 8회, db file sequential read
37. 다음 실행계획에 대한 설명으로 가장 적절한 것은?
---------------------------------------------------------- | Id | Operation | Name | ---------------------------------------------------------- | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | |* 2 | TABLE ACCESS FULL | 상품 | | 3 | NESTED LOOPS | | | 4 | NESTED LOOPS | | | 5 | TABLE ACCESS BY INDEX ROWID | 계약 | |* 6 | INDEX RANGE SCAN | 계약_X01 | |* 7 | INDEX RANGE SCAN | 담보_PK | | 8 | TABLE ACCESS BY INDEX ROWID | 담보 | ----------------------------------------------------------- 가장 먼저 실행되는 오퍼레이션은 가장 깊이 들여쓰기된 Id 6이다.
- 계약 테이블이 해시 조인의 Build Input이 된다.
- 담보 테이블은 NL 조인의 Outer(드라이빙) 테이블이다.
- 상품을 먼저 읽어 해시 테이블을 만든 뒤, 계약 → 담보 순의 NL 조인 결과를 Probe Input으로 하여 해시 조인한다.
38. 인덱스 청구_X01은 (계약번호, 청구일자, 처리상태)로 구성되어 있다. 다음 실행계획의 Predicate Information 해석으로 옳은 것은?
| Id | Operation | Name | | 0 | SELECT STATEMENT | | |* 1 | TABLE ACCESS BY INDEX ROWID BATCHED| 보험금청구 | |* 2 | INDEX RANGE SCAN | 청구_X01 | Predicate Information (identified by operation id): 1 - filter("청구금액">=1000000) 2 - access("계약번호"=:B1 AND "청구일자">=:B2 AND "처리상태"='03' AND "청구일자"<=:B3) filter("처리상태"='03')- 인덱스 스캔 범위는 계약번호와 청구일자 조건으로 정해지고, 처리상태는 인덱스 필터, 청구금액은 테이블 필터로 동작한다.
- 처리상태 조건이 access에 나타나므로 처리상태도 수평적 탐색 범위를 줄이는 데 기여한다.
- 청구금액 조건은 인덱스를 스캔하는 단계에서 걸러지므로 테이블 액세스 횟수를 줄여 준다.
- 인덱스를 (처리상태, 계약번호, 청구일자)로 바꾸어도 인덱스 스캔량은 변하지 않는다.
39. 다음은
DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')로 확인한 실행 통계이다. 해석으로 가장 적절한 것은?----------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers | ----------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 30 | 6,215 | | 1 | NESTED LOOPS | | 1 | 10 | 30 | 6,215 | | 2 | NESTED LOOPS | | 1 | 10 | 2,000 | 4,230 | | 3 | TABLE ACCESS BY INDEX ROWID| 계약 | 1 | 10 | 2,000 | 230 | |* 4 | INDEX RANGE SCAN | 계약_X02 | 1 | 10 | 2,000 | 12 | |* 5 | INDEX RANGE SCAN | 청구_X01 | 2,000 | 1 | 2,000 | 4,000 | |* 6 | TABLE ACCESS BY INDEX ROWID | 보험금청구 | 2,000 | 1 | 30 | 1,985 | ------------------------------------------------------------------------------------------ 계약 테이블의 E-Rows(10)와 A-Rows(2,000)가 일치하므로 카디널리티 추정이 정확하다.
- 보험금청구 테이블 액세스 자체에서 읽은 블록은 1,985개이며, 2,000건을 액세스해 30건만 남겼으므로 테이블 필터 컬럼을 청구_X01에 추가하면 랜덤 액세스를 크게 줄일 수 있다.
- 청구_X01 인덱스는 SQL 수행 중 1회만 탐색되었다.
- 이 SQL의 전체 논리적 I/O는 모든 오퍼레이션의 Buffers를 더한 값이다.
40. 다음은 청구 접수 화면에서 반복 호출되는 SQL의 TKPROF 결과이다. 해석으로 가장 적절한 것은?
call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 10000 1.20 1.35 0 0 0 0 Execute 10000 0.85 0.97 0 0 0 0 Fetch 10000 0.80 0.95 0 30000 0 10000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 30000 2.85 3.27 0 30000 0 10000 Misses in library cache during parse: 1- Parse Call이 10,000번이므로 하드 파싱도 10,000번 발생했다.
- Fetch 1회당 평균 30블록을 읽었다.
- 하드 파싱은 1회뿐이지만 실행할 때마다 Parse Call이 발생하고 있으므로, 애플리케이션에서 커서를 캐싱하여 한 번 Parse한 커서로 Execute를 반복하면 Parse Call을 줄일 수 있다.
- Misses in library cache 값으로 볼 때 이 SQL은 바인드 변수를 사용하지 않은 리터럴 SQL이다.
41. [보험금청구] 테이블에 단일 컬럼 인덱스 청구_X02(지급일자)가 있다. 지급일자는 지급 전에는 NULL이다. 다음 설명 중 옳은 것은? (Oracle 기준)
- 청구_X02는 지급일자가 NULL인 레코드를 저장하지 않으므로
WHERE 지급일자 IS NULL(미지급 건) 조건을 이 인덱스로 Range Scan 할 수 없다. - 미지급 건 삭제가 많이 발생하면 리프 블록마다 루트까지의 높이가 달라지는 Unbalanced Index가 된다.
- 인덱스 리프의 ROWID는 버퍼 캐시의 메모리 주소이므로 테이블 블록을 찾을 때 래치나 해시 탐색이 필요 없다.
- 브랜치 블록의 각 레코드는 테이블 레코드와 1:1로 대응한다.
- 청구_X02는 지급일자가 NULL인 레코드를 저장하지 않으므로
42. 보험사 시스템의 인덱스·저장 구조 선택에 대한 설명으로 가장 적절하지 않은 것은?
- 계약번호별 담보를 주로 범위 조회하는 [담보] 테이블을 (계약번호, 담보코드) PK의 IOT로 만들면 같은 계약의 담보가 모여 있어 랜덤 액세스가 줄어든다.
- 야간에만 적재되고 낮에는 조회만 하는 보험금 분석 마트의 성별·연령대·지역 컬럼에 비트맵 인덱스를 만들어 다양한 조건 조합 조회에 활용한다.
- SQL Server에서 클러스터형 인덱스가 있는 테이블의 비클러스터형 인덱스 리프에는 RID 대신 클러스터형 인덱스 키가 저장된다.
- 초당 수백 건의 INSERT·UPDATE가 발생하는 온라인 청구 접수 테이블의 처리상태 컬럼(값 5종)은 카디널리티가 낮으므로 비트맵 인덱스가 적합하다.
43. [계약] 테이블에 다음 인덱스가 있다. 각 조건 중 해당 인덱스로 Index Range Scan이 가능한 것을 모두 고른 것은?
ㄱ.
WHERE 계약일자 LIKE '202405%'ㄴ.WHERE SUBSTR(계약일자, 1, 6) = '202405'ㄷ.WHERE 계약일자 = 20240501ㄹ.WHERE 월보험료 = '50000'ㅁ.WHERE 월보험료 LIKE '5%'계약_X01 : (계약일자) -- 계약일자 VARCHAR2(8) 계약_X02 : (월보험료) -- 월보험료 NUMBER- ㄱ, ㄷ
- ㄱ, ㄹ
- ㄱ, ㄷ, ㄹ
- ㄱ, ㄹ, ㅁ
44. 인덱스 청구_X03은 (처리상태, 청구일자)로 구성되어 있고, 처리상태는 '01'~'05' 다섯 가지 값만 사용한다. 다음 SQL의 실행계획에 대한 설명으로 옳은 것은?
SELECT * FROM 보험금청구 WHERE 청구일자 BETWEEN '20240501' AND '20240507'; | Id | Operation | Name | | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| 보험금청구 | |* 2 | INDEX SKIP SCAN | 청구_X03 |- Index Skip Scan은 선두 컬럼(처리상태)의 Distinct 값이 많을수록 효과적이다.
- INDEX_FFS 힌트로 Index Fast Full Scan을 유도하면 결과가 청구일자 순으로 정렬되어 나온다.
- 선두 컬럼의 Distinct 값이 적어 Skip Scan이 효과적인 상황이며,
처리상태 IN ('01','02','03','04','05')조건을 추가하면 IN-List Iterator 방식으로 더 확실하게 처리할 수 있다. - 선두 컬럼 조건이 없으므로 이 인덱스는 Index Full Scan 외에는 어떤 방식으로도 사용할 수 없다.
45. [보험금청구] 테이블은 1,000,000건, 20,000블록이다. 인덱스 통계가 다음과 같을 때 설명으로 옳은 것은?
SELECT INDEX_NAME, CLUSTERING_FACTOR, NUM_ROWS, LEAF_BLOCKS FROM USER_INDEXES WHERE TABLE_NAME = '보험금청구'; INDEX_NAME CLUSTERING_FACTOR NUM_ROWS LEAF_BLOCKS ----------- ----------------- ---------- ----------- 청구_X01 21,000 1,000,000 2,600 -- (청구일자) 청구_X02 950,000 1,000,000 2,900 -- (사고자주민번호)- 같은 건수를 인덱스로 읽어 테이블을 액세스할 때, 청구_X01을 이용하는 쪽의 테이블 블록 I/O가 청구_X02보다 훨씬 적다.
- 청구_X02를 REBUILD 하면 클러스터링 팩터가 테이블 블록 수 근처로 개선된다.
- 클러스터링 팩터는 인덱스 비용 계산에 사용되지 않는 참고용 통계이다.
- 청구_X02는 클러스터링 팩터가 나쁘므로 청구_X01보다 인덱스 손익분기점이 더 높다.
46. 다음 SQL의 실행 통계를 보고 제시한 개선안 중 가장 적절한 것은? (청구_X01 = (청구일자))
SELECT 청구번호, 청구금액 FROM 보험금청구 WHERE 청구일자 BETWEEN :d1 AND :d2 AND 사고유형 = '교통'; | Id | Operation | Name | Starts | A-Rows | Buffers | | 0 | SELECT STATEMENT | | 1 | 12 | 8,350 | |* 1 | TABLE ACCESS BY INDEX ROWID| 보험금청구 | 1 | 12 | 8,350 | |* 2 | INDEX RANGE SCAN | 청구_X01 | 1 | 8,400 | 35 | Predicate Information 1 - filter("사고유형"='교통') 2 - access("청구일자">=:D1 AND "청구일자"<=:D2)- 테이블 액세스 후 버려지는 건이 많으므로 INDEX_FFS 힌트로 인덱스를 Multiblock I/O로 읽게 한다.
- 청구_X01 인덱스를 REBUILD 하여 인덱스 블록 수를 줄인다.
- 테이블 데이터를 사고유형 순으로 재정렬하여 청구_X01의 클러스터링 팩터를 개선한다.
- 청구_X01 뒤에 사고유형을 추가하면 인덱스 단계에서 걸러 테이블 랜덤 액세스가 약 12회로 줄고, 청구번호·청구금액까지 포함하면 테이블 액세스 없이 처리할 수 있다.
47. [보험금청구] 테이블에 대해 다음 세 SQL이 자주 수행된다. 인덱스를 하나만 만들어 세 SQL을 모두 효율적으로 처리하려 할 때 가장 적절한 컬럼 구성은? (계약당 청구는 수백 건까지 쌓이며, Q3은 소트 없이 처리되어야 한다)
-- Q1 (가장 빈번) WHERE 계약번호 = :ctr AND 청구일자 BETWEEN :d1 AND :d2 -- Q2 WHERE 계약번호 = :ctr AND 처리상태 = :st AND 청구일자 >= :d1 -- Q3 (최근 10건, ROWNUM 사용) WHERE 계약번호 = :ctr ORDER BY 청구일자 DESC- (처리상태, 계약번호, 청구일자)
- (계약번호, 처리상태, 청구일자)
- (계약번호, 청구일자, 처리상태)
- (청구일자, 계약번호, 처리상태)
48. 인덱스 지급_X01은 (지급유형, 지급일자)이고 지급유형은 '1', '2', '3' 세 가지 값만 존재한다. 다음 SQL에 대한 설명으로 옳은 것은?
SELECT 지급번호, 지급금액 FROM 지급 WHERE 지급유형 BETWEEN '1' AND '3' AND 지급일자 = :pay_dt;- 지급일자 조건이
=이므로 지급유형이 BETWEEN이어도 지급일자는 인덱스 스캔 범위를 줄이는 액세스 조건으로 동작한다. - 지급유형 조건을
IN ('1', '2', '3')으로 바꾸면 INLIST ITERATOR로 지급유형별 수직적 탐색을 3번 수행하며, 지급일자까지 액세스 조건이 되어 스캔 범위가 크게 줄어든다. - IN-List로 바꾸는 방식은 선두 컬럼의 값 종류가 많을수록 효과가 커진다.
- 인덱스를 (지급일자, 지급유형)으로 바꾸어도 이 SQL의 스캔 범위는 줄어들지 않는다.
- 지급일자 조건이
49. 다음 SQL과 TKPROF Row Source 통계를 보고 가장 효과적인 튜닝 방안을 고른 것은? (계약_X01 = (계약일자), 청구_X02 = (계약번호))
SELECT /*+ LEADING(A) USE_NL(C) INDEX(A 계약_X01) INDEX(C 청구_X02) */ A.계약번호, C.청구번호, C.청구금액 FROM 계약 A, 보험금청구 C WHERE A.계약일자 BETWEEN '20240101' AND '20240131' AND A.상품코드 = 'CI-301' AND C.계약번호 = A.계약번호 AND C.청구금액 >= 100000; Rows Row Source Operation ------- --------------------------------------------------------- 12 NESTED LOOPS (cr=4890 pr=1210 time=2410332 us) 3 TABLE ACCESS BY INDEX ROWID 계약 (cr=4862 pr=1195 ...) 4850 INDEX RANGE SCAN 계약_X01 (cr=15 pr=3 ...) 12 TABLE ACCESS BY INDEX ROWID 보험금청구 (cr=28 pr=15 ...) 15 INDEX RANGE SCAN 청구_X02 (cr=9 pr=4 ...)- 계약_X01 인덱스로 읽은 4,850건 중 3건만 남으므로, 계약_X01에 상품코드를 추가하거나 (상품코드, 계약일자) 인덱스를 만들어 테이블 액세스 전에 거른다.
- 보험금청구를 드라이빙 테이블로 바꾸도록 LEADING(C) USE_NL(A) 힌트를 준다.
- NL 조인 대신 USE_HASH(C) 힌트로 해시 조인을 유도한다.
- 청구_X02 인덱스에 청구금액을 추가해 Inner 쪽 랜덤 액세스를 줄인다.
50. NL 조인에 대한 설명 중 옳은 것을 모두 고른 것은?
ㄱ. 조인 조건이
BETWEEN이나LIKE같은 비등치 조건이어도 NL 조인을 사용할 수 있다. ㄴ. Inner 쪽 조인 컬럼에 인덱스가 없으면 Outer 건수만큼 Inner를 Full Scan 해야 하므로, 옵티마이저는 이런 경우 NL 조인을 거의 선택하지 않는다. ㄷ. 11g 이후 NESTED LOOPS가 두 번 나타나는 배치 I/O 실행계획에서는 Inner 쪽 결과가 항상 인덱스 키 순서대로 반환된다. ㄹ. 부분범위 처리에 유리하며, Outer 쪽을 ORDER BY 컬럼 순서와 같은 인덱스로 읽으면 SORT ORDER BY 없이 결과를 낼 수 있다.- ㄱ, ㄴ
- ㄴ, ㄹ
- ㄱ, ㄷ, ㄹ
- ㄱ, ㄴ, ㄹ
51. [계약](500만 건)의 월보험료를 [보험료구간](구간하한, 구간상한) 테이블과 조인해 구간별 등급을 붙이는 배치 SQL이다. 조인 컬럼에는 인덱스가 없다. 다음 설명 중 옳은 것은?
SELECT /*+ LEADING(A) USE_MERGE(G) FULL(A) FULL(G) */ A.계약번호, A.월보험료, G.등급코드 FROM 계약 A, 보험료구간 G WHERE A.월보험료 BETWEEN G.구간하한 AND G.구간상한; | Id | Operation | Name | | 0 | SELECT STATEMENT | | | 1 | MERGE JOIN | | | 2 | SORT JOIN | | | 3 | TABLE ACCESS FULL | 계약 | |* 4 | FILTER | | |* 5 | SORT JOIN | | | 6 | TABLE ACCESS FULL| 보험료구간 |- USE_HASH(G) 힌트를 주면 비등치 조건이라도 해시 조인으로 처리된다.
- MERGE JOIN의 Inner인 보험료구간 쪽은 구간하한 컬럼에 인덱스가 있으면 SORT JOIN이 생략된다.
- 정렬된 Outer(계약) 한 건마다 PGA에 정렬된 Inner(보험료구간)에서 시작점을 찾아 한쪽 범위 조건으로 머지하고, 나머지 범위 조건은 FILTER로 확인한다.
- 조인 조건을
A.등급코드 <> G.등급코드로 바꾸어도 같은 방식의 소트 머지 조인으로 처리할 수 있다.
52. 상품(P, 300건), 계약(A, 500만 건), 담보코드(D, 200건) 세 테이블을 해시 조인하는 SQL에서 아래 실행계획을 얻으려 한다. 사용할 힌트로 옳은 것은? (조인 조건: A.상품코드 = P.상품코드, A.주담보코드 = D.담보코드)
| Id | Operation | Name | | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | | 2 | TABLE ACCESS FULL | 담보코드 | |* 3 | HASH JOIN | | | 4 | TABLE ACCESS FULL | 상품 | | 5 | TABLE ACCESS FULL | 계약 |- /*+ LEADING(P A D) USE_HASH(A) USE_HASH(D) */
- /*+ LEADING(P A D) USE_HASH(A) USE_HASH(D) SWAP_JOIN_INPUTS(D) */
- /*+ LEADING(D P A) USE_HASH(P) USE_HASH(A) */
- /*+ LEADING(A P D) USE_HASH(P) USE_HASH(D) SWAP_JOIN_INPUTS(D) */
53. 월말 보험금 지급 집계 배치에서 다음 실행 통계를 확인했다. 해석과 조치로 가장 적절한 것은?
---------------------------------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | Reads | Used-Mem | Used-Tmp | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 120 | 215K | 398K | | | | 1 | HASH GROUP BY | | 120 | 215K | 398K | 1.2M | | |* 2 | HASH JOIN | | 880K | 215K | 398K | 48M | 1,850M | |* 3 | TABLE ACCESS FULL| 지급 | 9,012K| 182K | 182K | | | |* 4 | TABLE ACCESS FULL| 계약 | 301K | 33K | 33K | | | ----------------------------------------------------------------------------------- Build Input(지급, 약 900만 건)이 Hash Area를 넘어 양쪽 집합을 해시 파티션으로 나눠 Temp에 쓰고 파티션 짝 단위로 조인했다. 작은 쪽인 계약 집합이 Build Input이 되도록 SWAP_JOIN_INPUTS(A) 등으로 조정하는 것을 검토한다.
- 해시 테이블은 SGA에 생성되므로 Used-Tmp가 발생한 것은 버퍼 캐시가 부족하기 때문이다.
- Build Input의 해시 키 컬럼에 중복 값이 많을수록 해시 체인이 짧아져 Probe 성능이 좋아진다.
- 해시 조인은 Build가 끝나기 전에도 Probe 결과를 바로 반환하므로 부분범위 처리에 유리하다.
54. 다음 SQL과 실행계획에 대한 설명으로 옳은 것은? (상품은 300건, 계약 조건을 만족하는 건은 10만 건이며 그 상품코드 종류는 50개 정도다)
SELECT A.계약번호, A.월보험료 FROM 계약 A WHERE A.계약일자 BETWEEN '20240101' AND '20240630' AND EXISTS (SELECT /*+ NO_UNNEST */ 'X' FROM 상품 P WHERE P.상품코드 = A.상품코드 AND P.판매채널 = 'TM'); | Id | Operation | Name | | 0 | SELECT STATEMENT | | |* 1 | FILTER | | | 2 | TABLE ACCESS BY INDEX ROWID | 계약 | |* 3 | INDEX RANGE SCAN | 계약_X01 | |* 4 | TABLE ACCESS BY INDEX ROWID | 상품 | |* 5 | INDEX UNIQUE SCAN | 상품_PK |- 실행계획의 FILTER 방식은 서브쿼리 쪽 상품을 먼저 읽고 계약을 나중에 읽는 순서로도 수행할 수 있다.
- FILTER 방식은 메인 쿼리 한 건마다 서브쿼리에서 조건을 만족하는 모든 행을 끝까지 찾는다.
- PUSH_SUBQ 힌트는 서브쿼리를 Unnesting 할 때 UNNEST 힌트와 함께 사용한다.
- 메인 쿼리 한 건마다 서브쿼리를 수행하되 입력값(상품코드)별 결과를 캐싱하므로, 입력값 종류가 적은 이 경우 실제 서브쿼리 수행 횟수는 10만 번보다 훨씬 적다.
55. 대량 청구 건을 처리하는 아래 SQL은 스칼라 서브쿼리 때문에 NL 방식으로만 수행된다. 해시 조인이 가능하도록 조인으로 바꾸면서 결과가 항상 같도록 작성한 것은? (청구번호는 보험금청구의 PK이다)
SELECT C.청구번호, C.청구금액, (SELECT SUM(P.지급금액) FROM 지급 P WHERE P.청구번호 = C.청구번호) AS 지급합계 FROM 보험금청구 C WHERE C.청구일자 BETWEEN :d1 AND :d2;- SELECT C.청구번호, C.청구금액, P.지급합계 FROM 보험금청구 C, (SELECT 청구번호, SUM(지급금액) 지급합계 FROM 지급 GROUP BY 청구번호) P WHERE C.청구일자 BETWEEN :d1 AND :d2 AND P.청구번호 = C.청구번호;
- SELECT C.청구번호, C.청구금액, P.지급합계 FROM 보험금청구 C LEFT OUTER JOIN (SELECT 청구번호, SUM(지급금액) 지급합계 FROM 지급 GROUP BY 청구번호) P ON P.청구번호 = C.청구번호 WHERE C.청구일자 BETWEEN :d1 AND :d2;
- SELECT C.청구번호, C.청구금액, SUM(P.지급금액) 지급합계 FROM 보험금청구 C LEFT OUTER JOIN 지급 P ON P.청구번호 = C.청구번호 WHERE C.청구일자 BETWEEN :d1 AND :d2 AND P.지급금액 > 0 GROUP BY C.청구번호, C.청구금액;
- SELECT C.청구번호, C.청구금액, P.지급합계 FROM (SELECT 청구번호, SUM(지급금액) 지급합계 FROM 지급 GROUP BY 청구번호) P LEFT OUTER JOIN 보험금청구 C ON C.청구번호 = P.청구번호 WHERE C.청구일자 BETWEEN :d1 AND :d2;
56. [계약] 테이블 통계가 다음과 같고 히스토그램은 없다. 다음 SQL에 대해 옵티마이저가 추정하는 카디널리티로 가장 가까운 것은?
NUM_ROWS(계약) = 1,000,000 NUM_DISTINCT : 상품코드 = 200, 계약상태 = 4 SELECT * FROM 계약 WHERE 상품코드 = :p AND 계약상태 = :s AND 월보험료 > :m;- 1,250건
- 5,000건
- 약 63건
- 약 3건
57. 옵티마이저에 대한 설명으로 가장 적절한 것은?
- 청구 목록 첫 화면 20건을 빨리 보여주는 화면에는 FIRST_ROWS(20)이, 월말 보험금 집계 배치에는 ALL_ROWS가 적합하다. FIRST_ROWS로 최적화된 SQL의 결과를 끝까지 Fetch하면 ALL_ROWS보다 느려질 수 있다.
- CBO는 조인 테이블 수와 관계없이 가능한 모든 조인 순서를 빠짐없이 비교해 항상 최저 비용 계획을 찾는다.
- RBO는 테이블 크기와 데이터 분포를 반영해 인덱스 사용 여부를 결정한다.
- CBO는
시도 = '서울' AND 구 = '강남구'처럼 서로 연관된 조건의 상관관계를 자동으로 반영하므로 카디널리티를 과소 추정하지 않는다.
58. 다음 SQL에 대한 설명으로 옳은 것은?
SELECT A.계약번호, B.청구번호, B.청구금액 FROM 계약 A, 보험금청구 B WHERE A.계약일자 >= '20240101' AND B.계약번호(+) = A.계약번호 AND B.처리상태 = '지급';- 조건절 이행에 의해 계약 테이블에도
처리상태 = '지급'조건이 추가된다. - 아우터 조인이므로 청구가 없는 계약도 청구번호가 NULL인 상태로 결과에 포함된다.
- 아우터 조인은 조인 순서가 고정되므로 이 SQL은 항상 계약 테이블이 먼저 드라이빙된다.
- 보험금청구 쪽 처리상태 조건에 (+)가 없어 결과가 이너 조인과 같으므로, 옵티마이저는 이를 이너 조인으로 변환해 보험금청구를 먼저 읽는 계획도 고려할 수 있다.
- 조건절 이행에 의해 계약 테이블에도
59. 다음 SQL과 실행계획에 대한 설명으로 옳은 것은?
SELECT /*+ LEADING(A) USE_NL(C) */ A.계약번호, A.상품코드, C.청구건수, C.청구합계 FROM 계약 A, (SELECT /*+ NO_MERGE PUSH_PRED */ 계약번호, COUNT(*) 청구건수, SUM(청구금액) 청구합계 FROM 보험금청구 GROUP BY 계약번호) C WHERE A.고객번호 = :cust AND C.계약번호 = A.계약번호; | Id | Operation | Name | | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | TABLE ACCESS BY INDEX ROWID | 계약 | |* 3 | INDEX RANGE SCAN | 계약_X02 | | 4 | VIEW PUSHED PREDICATE | | | 5 | SORT GROUP BY | | | 6 | TABLE ACCESS BY INDEX ROWID| 보험금청구 | |* 7 | INDEX RANGE SCAN | 청구_X01 |- GROUP BY 인라인 뷰가 메인 쿼리와 Merging되어 실행계획에서 뷰가 사라졌다.
- 계약에서 읽은 계약번호가 뷰 안으로 조인 조건으로 밀려 들어가, 해당 계약의 청구만 인덱스로 읽어 집계한다.
- ROWNUM을 포함한 인라인 뷰도 같은 방식으로 Merging되어 처리된다.
- 조인 조건 Pushdown은 해시 조인에서만 동작한다.
60. 다음 SQL의 힌트에 대한 설명 중 옳은 것을 모두 고른 것은?
ㄱ. LEADING(계약)은 FROM 절에 Alias를 지정했는데 테이블명을 사용했으므로 무시된다. ㄴ. 무시되는 힌트가 있으면 Oracle은 파싱 단계에서 오류를 발생시킨다. ㄷ. USE_NL(B)는 B를 Inner로 하는 NL 조인을 지시할 뿐 조인 순서를 정하지 않으므로, 조인 순서가 지정되지 않은 이 SQL에서는 B가 먼저 드라이빙될 수도 있다. ㄹ. FULL(A)와 INDEX(A 계약_X01)을 함께 지정하면 두 지시가 모두 반영되어 인덱스 스캔 후 Full Scan을 수행한다.
SELECT /*+ LEADING(계약) USE_NL(B) FULL(A) INDEX(A 계약_X01) INDEX(B 청구_X01) */ A.계약번호, B.청구번호, B.청구금액 FROM 계약 A, 보험금청구 B WHERE A.계약일자 BETWEEN :d1 AND :d2 AND B.계약번호 = A.계약번호;- ㄱ, ㄴ
- ㄴ, ㄹ
- ㄱ, ㄷ
- ㄱ, ㄷ, ㄹ
61. 인덱스 청구_X01은 (계약번호, 청구일자)로 구성되어 있다(청구일자는 VARCHAR2(8)). 다음 SQL 중 SORT ORDER BY 없이 처리할 수 있는 것을 모두 고른 것은?
ㄱ.
WHERE 계약번호 = :c ORDER BY 청구일자 DESCㄴ.WHERE 계약번호 IN (:c1, :c2) ORDER BY 청구일자ㄷ.WHERE 계약번호 BETWEEN :c1 AND :c2 ORDER BY 계약번호, 청구일자ㄹ.SELECT SUBSTR(청구일자, 1, 6) AS 청구일자, ... WHERE 계약번호 = :c ORDER BY 청구일자- ㄱ, ㄷ
- ㄱ, ㄴ
- ㄱ, ㄴ, ㄷ
- ㄱ, ㄷ, ㄹ
62. 인덱스 청구_X01은 (계약번호, 청구일자)이다. 다음 페이징 SQL에 대한 설명으로 옳은 것은?
SELECT * FROM (SELECT ROWNUM NO, A.* FROM (SELECT 청구번호, 청구일자, 청구금액 FROM 보험금청구 WHERE 계약번호 = :ctr ORDER BY 청구일자 DESC) A) WHERE NO BETWEEN 11 AND 20;- 인덱스로 소트가 생략되므로 COUNT STOPKEY가 작동하여 20건만 읽고 멈춘다.
NO BETWEEN 11 AND 20조건 때문에 Top N 소트 알고리즘이 작동하여 상위 20건만 Sort Area에 유지한다.- ROWNUM을 사용하면 인덱스를 이용한 소트 생략이 불가능해진다.
- 정렬된 인라인 뷰 바로 바깥에
WHERE ROWNUM <= 20조건을 추가하면 COUNT STOPKEY로 20건만 읽고 멈출 수 있다.
63. 다음 문장을 수행했을 때의 설명으로 옳은 것은? (지급_이력은 LOGGING 모드의 일반 힙 테이블이다)
INSERT /*+ APPEND */ INTO 지급_이력 SELECT * FROM 지급 WHERE 지급일자 < '20240101'; | Id | Operation | Name | | 0 | INSERT STATEMENT | | | 1 | LOAD AS SELECT | 지급_이력 | |* 2 | TABLE ACCESS FULL| 지급 |- 커밋하기 전에도 같은 세션에서 지급_이력을 조회해 적재 결과를 확인할 수 있다.
- HWM 위쪽 새 블록에 버퍼 캐시를 거치지 않고 기록하며 테이블 데이터에 대한 Undo를 생성하지 않고, 커밋 전까지 다른 세션의 지급_이력 DML은 대기한다.
- 지급_이력을 NOLOGGING으로 바꾸면 이후 지급_이력에 대한 UPDATE·DELETE의 Redo도 최소화된다.
INSERT /*+ APPEND */ INTO 지급_이력 VALUES (...)처럼 VALUES 절 INSERT에 APPEND 힌트를 써도 같은 방식으로 동작한다.
64. [보험금청구] 테이블은 청구일자(VARCHAR2(8)) 기준 월 단위 Range 파티션 테이블이다. 다음 조건 중 파티션 Pruning이 일어나지 않는 것을 모두 고른 것은?
ㄱ.
WHERE 청구일자 BETWEEN '20240301' AND '20240331'ㄴ.WHERE 청구일자 BETWEEN :d1 AND :d2ㄷ.WHERE SUBSTR(청구일자, 1, 6) = '202403'ㄹ.WHERE 청구일자 = 20240315CREATE TABLE 보험금청구 ( 청구번호 NUMBER, 계약번호 VARCHAR2(10), 청구일자 VARCHAR2(8), 청구금액 NUMBER, ... ) PARTITION BY RANGE (청구일자) ( PARTITION P202401 VALUES LESS THAN ('20240201'), PARTITION P202402 VALUES LESS THAN ('20240301'), PARTITION P202403 VALUES LESS THAN ('20240401'), ... PARTITION PMAX VALUES LESS THAN (MAXVALUE) );- ㄷ
- ㄴ, ㄷ
- ㄷ, ㄹ
- ㄴ, ㄷ, ㄹ
65. 상품별 월 보험료 합계를 구하는 배치 SQL을 DOP 4로 병렬 수행한 실행계획이다. 설명으로 가장 적절한 것은?
-------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10002 | Q1,02| P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,02| PCWP | | | 4 | PX RECEIVE | | Q1,02| PCWP | | | 5 | PX SEND HASH | :TQ10001 | Q1,01| P->P | HASH | |* 6 | HASH JOIN | | Q1,01| PCWP | | | 7 | PX RECEIVE | | Q1,01| PCWP | | | 8 | PX SEND BROADCAST | :TQ10000 | Q1,00| P->P | BROADCAST | | 9 | PX BLOCK ITERATOR | | Q1,00| PCWC | | | 10 | TABLE ACCESS FULL | 상품 | Q1,00| PCWP | | | 11 | PX BLOCK ITERATOR | | Q1,01| PCWC | | | 12 | TABLE ACCESS FULL | 계약 | Q1,01| PCWP | | --------------------------------------------------------------------------- 큰 계약 테이블을 모든 병렬 서버에 복제(Broadcast)하여 조인한다.
- S->P 오퍼레이션이 있어 QC가 직렬로 처리하는 병목 구간이 존재한다.
- HASH GROUP BY는 QC가 직렬로 수행한다.
- 작은 상품 테이블을 Broadcast하고 계약은 재분배 없이 블록 단위로 나눠 읽어 조인하며, 조인 결과는 GROUP BY 키의 해시 값으로 재분배된다. P->P 재분배가 있으므로 병렬 서버는 DOP의 2배인 8개가 사용된다.
66. 다음 SQL은 같은 테이블을 세 번 읽는다. 세 건수를 한 번의 테이블 액세스로 정확히 구하는 SQL로 옳은 것은?
SELECT '접수' 구분, COUNT(*) 건수 FROM 보험금청구 WHERE 청구일자 = :dt AND 처리상태 = '01' UNION ALL SELECT '심사', COUNT(*) FROM 보험금청구 WHERE 청구일자 = :dt AND 처리상태 = '02' UNION ALL SELECT '지급', COUNT(*) FROM 보험금청구 WHERE 청구일자 = :dt AND 처리상태 = '03';- SELECT COUNT(CASE WHEN 처리상태 = '01' THEN 1 END) 접수, COUNT(CASE WHEN 처리상태 = '02' THEN 1 END) 심사, COUNT(CASE WHEN 처리상태 = '03' THEN 1 END) 지급 FROM 보험금청구 WHERE 청구일자 = :dt;
- SELECT COUNT(CASE WHEN 처리상태 = '01' THEN 1 ELSE 0 END) 접수, COUNT(CASE WHEN 처리상태 = '02' THEN 1 ELSE 0 END) 심사, COUNT(CASE WHEN 처리상태 = '03' THEN 1 ELSE 0 END) 지급 FROM 보험금청구 WHERE 청구일자 = :dt;
- SELECT SUM(CASE WHEN 처리상태 = '01' THEN 청구금액 END) 접수, SUM(CASE WHEN 처리상태 = '02' THEN 청구금액 END) 심사, SUM(CASE WHEN 처리상태 = '03' THEN 청구금액 END) 지급 FROM 보험금청구 WHERE 청구일자 = :dt;
- SELECT COUNT(CASE WHEN 처리상태 = '01' THEN 1 END) 접수, COUNT(CASE WHEN 처리상태 = '02' THEN 1 END) 심사, COUNT(CASE WHEN 처리상태 = '03' THEN 1 END) 지급 FROM 보험금청구 WHERE 청구일자 = :dt AND 처리상태 = '01' OR 처리상태 IN ('02', '03');
67. Oracle에서 세 세션이 다음 순서로 문장을 수행했다(모두 자동 커밋 아님, DDL_LOCK_TIMEOUT은 기본값 0). 설명으로 옳은 것은?
T1 세션A: UPDATE 계약 SET 계약상태 = '해지' WHERE 계약번호 = 'C1'; -- 기존 값 '유지' T2 세션B: SELECT 계약상태 FROM 계약 WHERE 계약번호 = 'C1'; T3 세션B: UPDATE 계약 SET 월보험료 = 52000 WHERE 계약번호 = 'C2'; T4 세션B: UPDATE 계약 SET 월보험료 = 61000 WHERE 계약번호 = 'C1'; T5 세션C: ALTER TABLE 계약 ADD (비고 VARCHAR2(100));- T2의 SELECT는 세션 A가 커밋할 때까지 대기한다.
- T3의 UPDATE는 세션 A가 계약 테이블에 TM Lock을 걸었으므로 대기한다.
- T2는 대기 없이 변경 전 값('유지')을 읽고, T3는 즉시 수행되며, T4는 세션 A가 커밋 또는 롤백할 때까지 대기한다.
- T5의 ALTER TABLE은 세션 A, B가 커밋할 때까지 기다렸다가 수행된다.
68. 여러 심사자가 동시에 '심사대기' 청구를 한 건씩 가져가 배정하는 기능을 만들고 있다. 다음 설명 중 가장 적절한 것은? (Oracle 기준)
SELECT 청구번호 FROM 보험금청구 WHERE 처리상태 = '심사대기' AND ROWNUM <= 1 FOR UPDATE SKIP LOCKED;- Oracle은 한 트랜잭션이 많은 행에 Lock을 걸면 메모리 절약을 위해 테이블 Lock으로 에스컬레이션하므로, 한 번에 여러 건을 가져가면 다른 심사자가 모두 대기한다.
- 아래처럼 FOR UPDATE SKIP LOCKED를 쓰면 다른 심사자가 Lock을 잡은 청구는 건너뛰고 Lock이 없는 청구를 가져가므로, 심사자들이 서로 대기하지 않고 작업을 나눠 가질 수 있다.
- 두 세션이 서로의 Lock을 기다리는 교착상태가 발생하면 Oracle은 한 세션의 트랜잭션 전체를 자동으로 롤백한다.
- 두 세션이 같은 PK 값으로 동시에 INSERT 하면 두 세션 모두 즉시 성공하고, 나중에 커밋하는 세션에서만 오류가 발생한다.
69. Oracle에서 다음 순서로 수행했을 때의 설명으로 옳은 것은? (계약 C1의 최초 월보험료는 50000)
T1 세션A: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; T2 세션A: SELECT 월보험료 FROM 계약 WHERE 계약번호 = 'C1'; -- 50000 T3 세션B: UPDATE 계약 SET 월보험료 = 60000 WHERE 계약번호 = 'C1'; COMMIT; T4 세션A: SELECT 월보험료 FROM 계약 WHERE 계약번호 = 'C1'; T5 세션A: UPDATE 계약 SET 월보험료 = 월보험료 * 1.1 WHERE 계약번호 = 'C1';- 세션 A의 T4 SELECT는 세션 B가 커밋한 60000을 읽는다(Non-Repeatable Read).
- 세션 A가 SERIALIZABLE이므로 T3에서 세션 B의 UPDATE는 세션 A가 끝날 때까지 대기한다.
- Oracle은 REPEATABLE READ 수준도 지원하므로 SERIALIZABLE 대신 REPEATABLE READ를 지정해도 같은 결과를 얻는다.
- 세션 A의 T4 SELECT는 트랜잭션 시작 시점 기준 값인 50000을 읽고, T5에서 같은 행을 UPDATE하면 ORA-08177 오류가 발생한다.
70. 보험금청구 심사 화면은 조회 시 Lock을 걸지 않고, 저장할 때 아래 UPDATE를 수행한다(:read_dt는 조회 시점에 읽어 둔 변경일시). 이에 대한 설명으로 가장 적절한 것은?
UPDATE 보험금청구 SET 처리상태 = '03', 승인금액 = :amt, 변경일시 = SYSDATE WHERE 청구번호 = :claim_no AND 변경일시 = :read_dt;- UPDATE 결과가 0건이면 조회 이후 다른 트랜잭션이 해당 청구를 변경·커밋한 것이므로 롤백 후 재조회하도록 처리해야 하며, 이 방식은 보험금청구를 갱신하는 모든 프로그램이 변경일시를 함께 갱신해야 유효하다.
- 이 방식은 조회 시점에 로우 Lock을 걸어 두는 비관적 동시성 제어이다.
- 변경일시 비교 대신 조회 시 SELECT ... FOR UPDATE를 사용하면 Lock 대기가 줄어 동시성이 높아진다.
- 다른 트랜잭션이 변경 후 아직 커밋하지 않았다면, 이 UPDATE는 대기하지 않고 즉시 0건으로 끝난다.
AI가 SQLP 출제 범위를 바탕으로 새로 만든 예상 문제이며, 실제 기출문제가 아닙니다.