SQLP 모의고사 3회
70문항 · 제한 시간 180분. 다 풀면 과목별 점수와 합격 여부, 문제마다 해설을 보여 줘요.
1. 다음 사례에서 가장 직접적으로 드러난 데이터 모델링의 중요성은?
K증권은 차세대 시스템 오픈 3주 전,
종목테이블의 주식별자를 '종목단축코드(6자리)'에서 '표준코드(12자리)'로 바꾸기로 결정하였다. 그 결과주문,체결,일별시세,잔고등 종목코드를 참조하는 수십 개 테이블과 수백 개 프로그램을 함께 수정해야 했고, 통합 테스트를 처음부터 다시 수행하느라 오픈 일정이 연기되었다.- 파급효과(Leverage): 데이터 모델의 변경은 시스템 구축 후반으로 갈수록 영향 범위와 비용이 커진다.
- 복잡한 정보 요구사항의 간결한 표현: 데이터 모델은 요구사항을 명확하고 간결하게 표현하는 도구다.
- 데이터 품질: 데이터 모델이 부실하면 동일 데이터가 서로 다른 값으로 중복 관리될 수 있다.
- 데이터 독립성: 내부 스키마를 바꾸어도 외부 스키마는 영향을 받지 않는다.
2. 증권사 업무를 분석하여 다음 엔터티를 도출하였다.
고객: 계좌 개설 시 등록되는 고객 정보종목: 거래소에 상장된 종목의 기본 정보계좌: 고객이 개설한 위탁 계좌주문: 계좌에서 종목을 매수·매도하겠다는 의사표시체결: 주문이 거래소에서 실제로 성사된 내역
발생 시점에 따른 엔터티 분류에 대한 설명으로 가장 적절하지 않은 것은?
종목은 다른 엔터티의 도움 없이 독립적으로 생성되고 고유한 주식별자를 가지므로 기본 엔터티이다.계좌는고객으로부터 발생하며주문등 많은 행위 엔터티를 생성하는 업무의 중심이므로 중심 엔터티이다.체결은주문하나로부터만 발생하므로 행위 엔터티가 될 수 없고 중심 엔터티로 분류해야 한다.주문은계좌와종목으로부터 발생하며 데이터가 자주 생성·변경되므로 행위 엔터티이다.
3. 증권 시스템의 속성 설계에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ.
체결엔터티의 '체결금액'은 체결수량 × 체결가격으로 계산되는 값이므로 파생 속성이다. - ㄴ.
계좌엔터티의 '계좌상태코드'(정상/정지/해지를 코드화)는 업무에 원래 존재하는 값이 아니라 설계 과정에서 정의한 값이므로 설계 속성이다. - ㄷ. 하나의 인스턴스에서 한 속성은 하나의 값만 가져야 하며, '보유종목'처럼 여러 값을 가져야 한다면 별도 엔터티로 분리하는 것을 검토한다.
- ㄹ.
종목엔터티에 주식별자 '종목코드' 외에 다른 속성이 없더라도, 다른 엔터티와 관계만 있으면 엔터티로 성립한다.
- ㄱ, ㄴ
- ㄱ, ㄷ
- ㄱ, ㄴ, ㄷ
- ㄱ, ㄴ, ㄷ, ㄹ
- ㄱ.
4. 다음 업무 규칙을 데이터 모델로 표현하고 물리 테이블로 구현하려 한다.
- 하나의 주문은 아직 체결되지 않았을 수도 있고(미체결), 여러 번에 나누어 체결될 수도 있다.
- 모든 체결은 반드시 하나의 주문에 대해 발생한다.
이에 대한 설명으로 가장 적절하지 않은 것은?
주문과체결은 1:M 관계이며, 체결 입장에서 주문과의 관계는 필수(Mandatory)이다.체결테이블의 주문번호 FK 컬럼은 NOT NULL로 정의하여 필수 참여를 물리적으로 구현할 수 있다.- 주문 쪽에서는 체결이 선택(Optional)이므로,
주문테이블에 체결번호 FK 컬럼을 두고 NULL을 허용해 관계를 구현한다. - 미체결 주문까지 포함해 주문별 체결 내역을 조회하려면
주문을 기준으로체결을 OUTER JOIN 해야 한다.
5. K증권의 주문 업무 규칙은 다음과 같다.
- 주문번호는 영업일마다 1부터 다시 부여된다. (같은 날짜 안에서는 유일)
- 하나의 주문은 여러 번 나누어 체결될 수 있으며, 체결은 주문 안에서 체결순번(1, 2, 3, …)으로 구분한다.
- 체결은 같은 시각(초 단위)에 여러 건이 발생할 수 있다.
식별자 설계에 대한 설명으로 가장 적절한 것은?
주문의 주식별자를 '주문번호' 단독으로 정의해도 유일성과 최소성을 모두 만족한다.주문의 주식별자를 (주문일자, 주문번호, 계좌번호)로 정의하면 유일성을 만족하므로 바람직한 주식별자이다.체결의 주식별자는 '체결시각'으로 정의하는 것이 업무적으로 가장 의미 있고 불변성도 만족한다.체결을주문과 식별 관계로 연결하여 주식별자를 (주문일자, 주문번호, 체결순번)으로 정의할 수 있다.
6. 다음
체결내역테이블의 PK는 (주문일자, 주문번호, 체결순번)이다. 업무 규칙상 함수 종속은 아래와 같다.체결내역(<u>주문일자</u>, <u>주문번호</u>, <u>체결순번</u>, 체결수량, 계좌번호, 고객번호, 고객명, 종목코드, 종목명)
- (주문일자, 주문번호, 체결순번) → 체결수량
- (주문일자, 주문번호) → 계좌번호, 종목코드
- 계좌번호 → 고객번호
- 고객번호 → 고객명
- 종목코드 → 종목명
모든 컬럼은 원자값만 가진다. 이 테이블이 현재 만족하는 가장 높은 정규형과, 3정규형까지 정규화했을 때의 테이블 수로 옳은 것은? (무손실 분해이며 불필요한 테이블은 만들지 않는다.)
- 1정규형, 4개
- 1정규형, 5개
- 2정규형, 5개
- 2정규형, 4개
7.
종목–주문–체결은 각각 1:M 관계이며 데이터는 다음과 같다. SQL의 실행 결과에서 '삼성전자'와 'NAVER' 행의 (주문건수, 체결건수)로 옳은 것은?[종목]
종목코드 종목명 005930 삼성전자 000660 SK하이닉스 035420 NAVER [주문]
주문번호 종목코드 101 005930 102 005930 103 000660 104 000660 [체결]
체결번호 주문번호 체결수량 1 101 10 2 101 5 3 103 7 SELECT S.종목명, COUNT(O.주문번호) AS 주문건수, COUNT(C.체결번호) AS 체결건수 FROM 종목 S LEFT OUTER JOIN 주문 O ON O.종목코드 = S.종목코드 LEFT OUTER JOIN 체결 C ON C.주문번호 = O.주문번호 GROUP BY S.종목명;- 삼성전자 (2, 2), NAVER (1, 0)
- 삼성전자 (3, 2), NAVER (0, 0)
- 삼성전자 (2, 2), NAVER (0, 0)
- 삼성전자 (3, 3), NAVER (1, 1)
8. 매수 주문이 체결되면 다음 세 작업이 수행된다. 모델은 IE 표기법으로
주문 ||--o{ 체결,계좌 ||--o{ 잔고와 같이 작성되어 있다.체결테이블에 체결 1건 INSERT주문테이블의 누적체결수량 UPDATE잔고테이블의 보유수량 UPDATE (해당 종목 잔고가 없으면 INSERT)
이에 대한 설명으로 옳은 것을 모두 고른 것은?
- ㄱ. 세 작업은 하나의 논리적 작업 단위이므로 하나의 트랜잭션으로 묶고, 모두 성공한 뒤 한 번에 COMMIT 해야 한다.
- ㄴ.
주문과체결은 주문 쪽에서 선택 관계이므로, 주문을 접수(INSERT)하는 트랜잭션에서 체결 행을 함께 생성할 필요는 없다. - ㄷ. 3번 UPDATE가 실패했을 때 1·2번만 반영하고 COMMIT한 뒤 사후 배치로 잔고를 보정하는 방식은 원자성(Atomicity)을 해친다.
- ㄹ. 잔고 갱신 중 다른 세션이 중간 상태를 읽지 못하게 하는 것은 트랜잭션의 지속성(Durability)에 해당한다.
- ㄱ, ㄴ
- ㄱ, ㄴ, ㄷ
- ㄴ, ㄷ, ㄹ
- ㄱ, ㄷ, ㄹ
9.
주문테이블의 주문가격은 시장가 주문일 때 NULL이다. (가)~(다) SQL의 결과 건수로 옳은 것은? (Oracle 기준)[주문]
주문번호 매매구분 주문가격 1 매수 1000 2 매도 NULL 3 매수 2000 4 매도 1000 5 매수 NULL -- (가) SELECT COUNT(*) FROM 주문 WHERE 주문가격 <> 1000 OR 주문가격 IS NULL; -- (나) SELECT COUNT(*) FROM 주문 WHERE NOT (주문가격 = 1000); -- (다) SELECT COUNT(*) FROM 주문 WHERE 주문가격 NOT IN (SELECT 주문가격 FROM 주문 WHERE 매매구분 = '매도');- (가) 3, (나) 1, (다) 0
- (가) 3, (나) 3, (다) 1
- (가) 1, (나) 1, (다) 0
- (가) 3, (나) 1, (다) 1
10.
일별시세엔터티의 본질식별자는 (종목코드, 거래일자)이다. 설계자는 이를 대신해 시퀀스로 생성하는 '시세ID'를 PK로 쓰려 한다. 조회는 대부분WHERE 종목코드 = :c AND 거래일자 BETWEEN :d1 AND :d2형태이다. 이에 대한 설명으로 옳은 것을 모두 고른 것은?- ㄱ. 시세ID만 PK로 두면 같은 (종목코드, 거래일자) 시세가 중복 입력되는 것을 DBMS가 막지 못하므로, 본질식별자에 Unique 제약을 별도로 두어야 한다.
- ㄴ. 주요 조회 조건이 종목코드·거래일자이므로 시세ID PK 인덱스 외에 (종목코드, 거래일자) 인덱스가 추가로 필요해져 인덱스 수가 늘어난다.
- ㄷ. 하위 엔터티가 없고 본질식별자가 2개 컬럼으로 짧으므로 인조식별자 도입의 이점은 크지 않다.
- ㄹ. 인조식별자를 사용하면 본질식별자 컬럼은 더 이상 NOT NULL일 필요가 없다.
- ㄱ, ㄴ
- ㄱ, ㄴ, ㄷ
- ㄴ, ㄷ, ㄹ
- ㄱ, ㄷ
11.
종목(종목코드, 종목명, 시장구분, 상장일자) 테이블에 대해 Oracle에서 실행할 때 오류가 발생하는 SQL은?- SELECT 종목코드 AS 코드, 종목명 FROM 종목 ORDER BY 코드, 2;
- SELECT 시장구분, COUNT() FROM 종목 GROUP BY 시장구분 ORDER BY COUNT() DESC;
- SELECT 종목명 FROM 종목 ORDER BY 상장일자 DESC;
- SELECT DISTINCT 시장구분 FROM 종목 ORDER BY 종목명;
12. 다음 SQL의 실행 결과로 옳은 것은? (Oracle 기준, C1 / C2 / C3 / C4 순서)
SELECT SUBSTR('KR7005930003', 4, 6) AS C1, INSTR('2026-10-20', '-', -1) AS C2, LPAD(RTRIM('593 '), 6, '0') AS C3, LENGTH(REPLACE('A B C', ' ')) AS C4 FROM DUAL;- 005930 / 8 / 000593 / 3
- 005930 / 5 / 593000 / 3
- 059300 / 8 / 000593 / 5
- 005930 / 5 / 000593 / 5
13. 다음 SQL의 실행 결과로 옳은 것은? (Oracle 기준, C1 / C2 / C3 / C4 순서)
SELECT TRUNC(12345.678, -2) AS C1, ROUND(-2.5) AS C2, CEIL(-2.5) AS C3, MOD(-7, 3) AS C4 FROM DUAL;- 12300 / -2 / -2 / 2
- 12400 / -3 / -3 / -1
- 12300 / -3 / -2 / -1
- 12300 / -3 / -3 / 2
14. 다음 SQL의 실행 결과로 조회되는 주문번호를 모두 나열한 것은?
[주문]
주문번호 매매구분 주문수량 종목코드 1 매수 100 005930 2 매도 50 005930 3 매수 200 000660 4 매도 300 000660 5 매수 30 035420 6 매도 150 035420 SELECT 주문번호 FROM 주문 WHERE 매매구분 = '매수' OR 매매구분 = '매도' AND 주문수량 BETWEEN 100 AND 300 AND 종목코드 <> '000660' ORDER BY 주문번호;- 1, 6
- 1, 3, 5
- 1, 3, 5, 6
- 1, 3, 4, 5, 6
15. 다음 SQL의 실행 결과로 옳은 것은? (결과는 계좌번호(CNT, S) 형식으로 출력 순서대로 표기)
[주문]
계좌번호 매매구분 주문수량 A1 매수 100 A1 매수 200 A1 매도 50 A2 매수 300 A2 매수 100 A3 매도 400 A3 매도 100 A3 매수 10 A4 매도 NULL A4 매도 120 SELECT 계좌번호, COUNT(*) AS CNT, SUM(주문수량) AS S FROM 주문 GROUP BY 계좌번호 HAVING COUNT(CASE WHEN 매매구분 = '매도' THEN 1 END) >= 1 AND AVG(주문수량) >= 100 ORDER BY 계좌번호;- A1(3, 350), A3(3, 510)
- A1(3, 350), A3(3, 510), A4(1, 120)
- A1(3, 350), A2(2, 400), A3(3, 510), A4(2, 120)
- A1(3, 350), A3(3, 510), A4(2, 120)
16. 다음 SQL의 실행 결과로 출력되는 종목명의 순서로 옳은 것은? (Oracle 기준)
[종목]
종목명 시장구분 PER A KOSPI 12 B NULL 8 C KOSDAQ NULL D KOSPI NULL E KOSDAQ 15 SELECT 종목명 FROM 종목 ORDER BY 시장구분 DESC, PER;- D, A, C, E, B
- B, A, D, E, C
- B, D, A, C, E
- A, D, E, C, B
17. 주문금액에 따라 수수료 등급을 매기려 한다. 다음 SQL의 결과 건수는?
[수수료등급]
등급 하한금액 상한금액 1 0 1000000 2 1000000 5000000 3 5000000 99999999 [주문]
주문번호 주문금액 1 500000 2 1000000 3 5000000 4 7000000 SELECT O.주문번호, G.등급 FROM 주문 O, 수수료등급 G WHERE O.주문금액 BETWEEN G.하한금액 AND G.상한금액;- 4건
- 5건
- 6건
- 8건
18. 다음 중 나머지 셋과 결과가 다른 SQL은? (Oracle 기준)
[계좌]
계좌번호 고객명 A1 김 A2 이 A3 박 [주문]
주문번호 계좌번호 상태 1 A1 체결 2 A1 접수 3 A2 접수 4 A3 체결 5 A3 체결 - SELECT A.계좌번호, O.주문번호 FROM 계좌 A LEFT OUTER JOIN 주문 O ON A.계좌번호 = O.계좌번호 AND O.상태 = '체결';
- SELECT A.계좌번호, O.주문번호 FROM 계좌 A LEFT OUTER JOIN 주문 O ON A.계좌번호 = O.계좌번호 WHERE O.상태 = '체결';
- SELECT A.계좌번호, O.주문번호 FROM 계좌 A LEFT OUTER JOIN (SELECT * FROM 주문 WHERE 상태 = '체결') O ON A.계좌번호 = O.계좌번호;
- SELECT A.계좌번호, O.주문번호 FROM 계좌 A, 주문 O WHERE A.계좌번호 = O.계좌번호(+) AND O.상태(+) = '체결';
19. 다음 (가)~(다) SQL의 결과 건수로 옳은 것은? (Oracle 기준,
종목코드 = '999999'인 주문은 없다.)[주문]
주문번호 종목코드 매매구분 주문수량 1 005930 매수 100 2 005930 매도 50 3 000660 매수 200 4 000660 매도 NULL 5 035420 매수 30 -- (가) SELECT * FROM 주문 WHERE 주문수량 > ALL (SELECT 주문수량 FROM 주문 WHERE 종목코드 = '999999'); -- (나) SELECT * FROM 주문 WHERE 주문수량 > ANY (SELECT 주문수량 FROM 주문 WHERE 종목코드 = '999999'); -- (다) SELECT * FROM 주문 WHERE 주문수량 > ALL (SELECT 주문수량 FROM 주문 WHERE 매매구분 = '매도');- (가) 0, (나) 0, (다) 0
- (가) 5, (나) 0, (다) 2
- (가) 5, (나) 0, (다) 0
- (가) 0, (나) 5, (다) 2
20. 다음 SQL의 실행 결과로 옳은 것은? (결과는 (종목코드, 거래일자)로 표기)
[일별시세]
종목코드 거래일자 종가 A 1 100 A 2 110 A 3 120 B 1 50 B 2 50 B 3 80 SELECT A.종목코드, A.거래일자 FROM 일별시세 A WHERE A.종가 > (SELECT AVG(B.종가) FROM 일별시세 B WHERE B.종목코드 = A.종목코드 AND B.거래일자 < A.거래일자) ORDER BY A.종목코드, A.거래일자;- (A, 3), (B, 3)
- (A, 2), (A, 3), (B, 3)
- (A, 1), (A, 2), (A, 3), (B, 3)
- (A, 2), (A, 3), (B, 2), (B, 3)
21. 관심종목 테이블 세 개의 데이터가 다음과 같다. (가), (나) SQL의 결과 건수로 옳은 것은? (Oracle 기준)
관심A.종목코드 관심B.종목코드 관심C.종목코드 005930 005930 000660 000660 035420 005930 -- (가) SELECT 종목코드 FROM 관심A UNION ALL SELECT 종목코드 FROM 관심B MINUS SELECT 종목코드 FROM 관심C; -- (나) SELECT 종목코드 FROM 관심A MINUS SELECT 종목코드 FROM 관심C UNION ALL SELECT 종목코드 FROM 관심B;- (가) 2, (나) 3
- (가) 3, (나) 3
- (가) 4, (나) 3
- (가) 2, (나) 4
22. 다음 SQL의 결과에 포함되는 행은? (시장 | 매매구분 | 금액 | GID 순서로 표기)
[체결]
시장 매매구분 금액 KOSPI 매수 100 KOSPI 매도 200 KOSPI 매수 50 KOSDAQ 매수 30 SELECT 시장, 매매구분, SUM(금액) AS 금액, GROUPING_ID(시장, 매매구분) AS GID FROM 체결 GROUP BY CUBE(시장, 매매구분) HAVING GROUPING_ID(시장, 매매구분) IN (1, 2);KOSPI | NULL | 350 | 2NULL | NULL | 380 | 3NULL | 매도 | 200 | 1NULL | 매수 | 180 | 2
23. 다음 SQL의 실행 결과에서 S1, S2 값을 출력 순서대로 나열한 것으로 옳은 것은?
[체결]
체결번호 체결시각 수량 1 09:00 10 2 09:01 20 3 09:01 30 4 09:02 40 SELECT 체결번호, SUM(수량) OVER (ORDER BY 체결시각) AS S1, SUM(수량) OVER (ORDER BY 체결시각, 체결번호 ROWS UNBOUNDED PRECEDING) AS S2 FROM 체결 ORDER BY 체결시각, 체결번호;- S1: 10, 30, 60, 100 / S2: 10, 30, 60, 100
- S1: 10, 60, 60, 100 / S2: 10, 60, 60, 100
- S1: 10, 60, 60, 100 / S2: 10, 30, 60, 100
- S1: 10, 30, 60, 100 / S2: 10, 60, 60, 100
24. 다음 SQL의 실행 결과에서 거래일자가 3인 행의 (F, L, M) 값으로 옳은 것은?
[일별시세] (한 종목의 데이터)
거래일자 종가 1 100 2 120 3 90 4 130 5 110 SELECT 거래일자, FIRST_VALUE(종가) OVER (ORDER BY 거래일자) AS F, LAST_VALUE(종가) OVER (ORDER BY 거래일자) AS L, MAX(종가) OVER (ORDER BY 거래일자 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS M FROM 일별시세;- (100, 110, 130)
- (100, 90, 120)
- (90, 110, 130)
- (100, 90, 130)
25. 다음 (가)~(라) SQL의 결과 건수로 옳은 것은? (Oracle 12c 이상 기준)
[거래순위]
종목 거래량 A 500 B 400 C 400 D 300 E 300 F 200 -- (가) SELECT * FROM (SELECT * FROM 거래순위 ORDER BY 거래량 DESC) WHERE ROWNUM <= 3; -- (나) SELECT * FROM (SELECT 종목, RANK() OVER (ORDER BY 거래량 DESC) R FROM 거래순위) WHERE R <= 3; -- (다) SELECT * FROM (SELECT 종목, DENSE_RANK() OVER (ORDER BY 거래량 DESC) R FROM 거래순위) WHERE R <= 3; -- (라) SELECT * FROM 거래순위 ORDER BY 거래량 DESC FETCH FIRST 2 ROWS WITH TIES;- 3, 4, 5, 2
- 3, 3, 5, 3
- 3, 3, 4, 3
- 3, 5, 5, 3
26. 증권사 영업조직이 다음과 같다. (가), (나) SQL의 결과 건수로 옳은 것은? (Oracle 기준)
[조직]
ID 조직명 상위ID 1 본사 NULL 2 서울본부 1 3 부산본부 1 4 강남지점 2 5 여의도지점 2 6 해운대지점 3 7 강남WM센터 4 -- (가) SELECT 조직명, LEVEL FROM 조직 START WITH ID = 1 CONNECT BY PRIOR ID = 상위ID AND 조직명 <> '서울본부'; -- (나) SELECT 조직명, LEVEL FROM 조직 WHERE 조직명 <> '서울본부' START WITH ID = 1 CONNECT BY PRIOR ID = 상위ID;- (가) 6, (나) 3
- (가) 3, (나) 3
- (가) 6, (나) 6
- (가) 3, (나) 6
27. 다음 (가)의 결과 건수와, (나)의 결과에서 계좌번호가 A2인 행의 (매수, 매도) 값으로 옳은 것은? (Oracle 기준)
[체결]
체결번호 계좌번호 매매구분 수량 1 A1 매수 10 2 A1 매도 5 3 A1 매수 20 4 A2 매수 7 -- (가) SELECT * FROM 체결 PIVOT (SUM(수량) FOR 매매구분 IN ('매수' AS 매수, '매도' AS 매도)); -- (나) SELECT * FROM (SELECT 계좌번호, 매매구분, 수량 FROM 체결) PIVOT (SUM(수량) FOR 매매구분 IN ('매수' AS 매수, '매도' AS 매도));- (가) 4건 / A2 (7, NULL)
- (가) 2건 / A2 (7, 0)
- (가) 2건 / A2 (7, NULL)
- (가) 4건 / A2 (7, 0)
28. 당일 체결 결과를 잔고에 반영하는 MERGE 문을 실행하였다. 실행 후
잔고테이블의 행 수와 수량 합계로 옳은 것은? (Oracle 기준)[잔고] (실행 전)
계좌 종목 수량 A1 X 100 A1 Y 50 A2 X 30 A3 X 0 [당일체결]
계좌 종목 순매수량 A1 X -100 A1 Y 20 A2 X -10 A2 Z 40 MERGE INTO 잔고 T USING 당일체결 S ON (T.계좌 = S.계좌 AND T.종목 = S.종목) WHEN MATCHED THEN UPDATE SET T.수량 = T.수량 + S.순매수량 DELETE WHERE T.수량 = 0 WHEN NOT MATCHED THEN INSERT (계좌, 종목, 수량) VALUES (S.계좌, S.종목, S.순매수량);- 3건, 130
- 4건, 130
- 5건, 130
- 4건, 230
29.
관심종목테이블에는 커밋된 2건이 있다. 다음 문장을 차례로 실행한 뒤 마지막 SELECT의 결과는? (Oracle 기준, 각 DML은 정상 수행된다.)INSERT INTO 관심종목 VALUES ('A'); SAVEPOINT SP1; DELETE FROM 관심종목 WHERE 종목코드 = '005930'; -- 1건 삭제 SAVEPOINT SP2; UPDATE 관심종목 SET 종목코드 = 'Z' WHERE 종목코드 = 'A'; ROLLBACK TO SP1; INSERT INTO 관심종목 VALUES ('B'); CREATE TABLE 관심종목_백업 (종목코드 VARCHAR2(12)); INSERT INTO 관심종목 VALUES ('C'); ROLLBACK; SELECT COUNT(*) FROM 관심종목;- 2
- 3
- 4
- 5
30. Oracle의 DDL에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ.
CREATE TABLE 주문_백업 AS SELECT * FROM 주문;으로 만든 테이블에는 원본의 NOT NULL 제약은 복사되지만 PK·FK·UNIQUE 제약과 인덱스는 복사되지 않는다. - ㄴ. TRUNCATE TABLE은 롤백할 수 없으며, 테이블의 HWM(High Water Mark)을 초기화해 저장 공간을 반환한다.
- ㄷ. 데이터가 있는 테이블에 DEFAULT 없이 NOT NULL 컬럼을 추가(
ALTER TABLE ... ADD)하면 오류가 발생한다. - ㄹ.
ALTER TABLE ... MODIFY로 컬럼 길이를 줄이는 것은 기존 데이터와 관계없이 항상 오류가 발생한다.
- ㄱ, ㄴ
- ㄴ, ㄷ
- ㄱ, ㄴ, ㄷ
- ㄱ, ㄴ, ㄷ, ㄹ
- ㄱ.
31. 다음은 고객의 예수금을 출금하는 트랜잭션이다. 이 트랜잭션이 처리되는 과정에 대한 설명으로 가장 적절하지 않은 것은? (Oracle 기준)
UPDATE 계좌 SET 예수금 = 예수금 - 1000000 WHERE 계좌번호 = 'A1001'; COMMIT;- COMMIT이 완료되려면 DBWn이 변경된 계좌 블록(더티 블록)을 데이터 파일에 먼저 기록해야 한다.
- UPDATE 시 변경 전 이미지는 Undo 세그먼트에 기록되며, 이 Undo 변경 내역도 Redo로 남는다.
- COMMIT 시 LGWR가 리두 로그 버퍼의 내용을 리두 로그 파일에 기록하면 커밋이 완료된 것으로 본다.
- 커밋 후 데이터 파일에 반영되기 전에 인스턴스가 비정상 종료되어도, 재기동 시 Redo로 Roll Forward하여 변경을 복구할 수 있다.
32. Oracle의 데이터 저장 구조에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ. 블록은 I/O의 최소 단위이므로
주문테이블에서 레코드 하나만 읽어도 그 레코드가 담긴 블록 전체를 읽는다. - ㄴ. 익스텐트는 인접한 블록의 집합이며, 하나의 익스텐트가 여러 데이터 파일에 걸쳐 저장될 수는 없다.
- ㄷ. 하나의 테이블스페이스가 여러 데이터 파일로 구성되면, 하나의 세그먼트가 여러 데이터 파일에 걸쳐 저장될 수 있다.
- ㄹ.
주문테이블을 월별 12개 파티션으로 나누어도 테이블은 하나이므로 세그먼트도 하나만 생성된다.
- ㄱ, ㄴ
- ㄱ, ㄹ
- ㄴ, ㄷ, ㄹ
- ㄱ, ㄴ, ㄷ
- ㄱ. 블록은 I/O의 최소 단위이므로
33. 같은 사용자가 다음 SQL을 순서대로 처음 실행하였다. 라이브러리 캐시에 SQL이 없던 상태에서 시작하며, 캐시에서 밀려나는 SQL은 없다고 가정한다. 하드 파싱이 발생한 횟수는? (Oracle 기준)
① SELECT * FROM 주문 WHERE 주문번호 = :1 ② select * from 주문 where 주문번호 = :1 ③ SELECT * FROM 주문 WHERE 주문번호 = :B1 ④ SELECT * FROM 주문 WHERE 주문번호 = :1 ⑤ SELECT * FROM 주문 WHERE 주문번호 = 1001 ⑥ SELECT * FROM 주문 WHERE 주문번호 = 1002 ⑦ SELECT * FROM 주문 WHERE 주문번호 = :1- 3회
- 4회
- 5회
- 6회
34.
주문테이블의 주문상태 컬럼은 값 분포가 '체결' 95%, '접수' 4.9%, '거부' 0.1%로 심하게 치우쳐 있고, 히스토그램이 수집되어 있다. 바인드 변수와 리터럴 상수의 사용에 대한 설명으로 가장 적절하지 않은 것은?- 바인드 변수 Peeking은 첫 수행 시점의 바인드 값으로 실행계획을 만들기 때문에, 이후 분포가 전혀 다른 값이 입력되어도 그 값에 맞는 최적 계획이 보장된다.
- 초당 수천 번 수행되는 '계좌별 미체결 주문 조회' 같은 OLTP SQL은 바인드 변수를 써서 하드 파싱을 줄이는 것이 원칙이다.
- 하루 몇 번 수행되고 오래 걸리는 '거부 주문 분석' 배치 SQL은 주문상태 조건에 리터럴 상수를 써서 히스토그램을 활용하게 하는 것이 유리할 수 있다.
- Peeking 없이 바인드 변수를 쓰면 옵티마이저는 히스토그램을 쓰지 못하고 균등 분포를 가정해 선택도를 1/NDV로 추정한다.
35. 다음은 어떤 SQL을 AutoTrace로 수행한 결과의 실행 통계이다. 이에 대한 설명으로 가장 적절한 것은?
Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 24000 consistent gets 6000 physical reads 0 redo size 8215 bytes sent via SQL*Net to client 520 bytes received via SQL*Net from client 11 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 150 rows processed- 논리적 I/O 24,000블록 중 6,000블록을 디스크에서 읽었으므로 버퍼캐시 히트율(BCHR)은 75%이다.
- 같은 SQL을 곧바로 다시 수행하면 버퍼 캐시에 블록이 적재되어 있으므로 consistent gets가 크게 줄어든다.
- 버퍼 캐시 크기를 늘리면 physical reads와 함께 consistent gets도 비례하여 줄어든다.
- physical reads가 0이 될 때까지 반복 수행해 캐시에 올려 두면 이 SQL은 더 이상 튜닝할 필요가 없다.
36. Single Block I/O와 Multiblock I/O에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ.
db file sequential read대기 이벤트는 주로 인덱스 스캔과 인덱스 ROWID에 의한 테이블 액세스처럼 Single Block I/O에서 발생한다. - ㄴ. Index Fast Full Scan은 인덱스 세그먼트 전체를 Multiblock I/O 방식으로 읽는다.
- ㄷ. Index Range Scan은 리프 블록을 키 순서대로 연속해서 읽으므로 Multiblock I/O 방식으로 읽는다.
- ㄹ.
db_file_multiblock_read_count가 16이면, 익스텐트 경계와 관계없이 한 번의 I/O Call로 항상 16블록씩 읽는다.
- ㄱ, ㄷ
- ㄱ, ㄴ, ㄹ
- ㄴ, ㄷ, ㄹ
- ㄱ, ㄴ
- ㄱ.
37. 다음 실행계획에서 오퍼레이션이 수행되는 순서로 옳은 것은? (Id 번호로 표기)
-------------------------------------------------------------- | Id | Operation | Name | Rows | -------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1200 | |* 1 | HASH JOIN | | 1200 | | 2 | NESTED LOOPS | | 300 | | 3 | TABLE ACCESS BY INDEX ROWID | 계좌 | 300 | |* 4 | INDEX RANGE SCAN | 계좌_X01 | 300 | | 5 | TABLE ACCESS BY INDEX ROWID | 고객 | 1 | |* 6 | INDEX UNIQUE SCAN | 고객_PK | 1 | |* 7 | TABLE ACCESS FULL | 주문 | 85000 | --------------------------------------------------------------- 7 → 4 → 3 → 6 → 5 → 2 → 1
- 4 → 3 → 6 → 5 → 2 → 7 → 1
- 4 → 6 → 3 → 5 → 2 → 7 → 1
- 3 → 4 → 5 → 6 → 2 → 7 → 1
38.
주문_X01인덱스는 (계좌번호, 주문일자, 종목코드)로 구성되어 있다. 다음 실행계획과 Predicate Information에 대한 설명으로 가장 적절한 것은?---------------------------------------------------------------- | Id | Operation | Name | Rows | ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 15 | |* 1 | TABLE ACCESS BY INDEX ROWID BATCHED| 주문 | 15 | |* 2 | INDEX RANGE SCAN | 주문_X01 | 120 | ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("O"."매매구분"='2') 2 - access("O"."계좌번호"=:ACNT AND "O"."주문일자">='20261001' AND "O"."주문일자"<='20261031') filter("O"."종목코드"='005930')- 종목코드 조건은 인덱스 컬럼이므로 계좌번호·주문일자와 함께 인덱스 스캔 범위를 줄이는 데 사용되었다.
- 종목코드 조건은 인덱스 filter로 처리되었으므로 테이블 랜덤 액세스 횟수를 줄이는 데에도 효과가 없다.
- Id 1의 매매구분 filter 조건은 인덱스 리프 블록에서 평가되어, 조건을 만족하지 않는 행은 테이블을 읽지 않는다.
- 매매구분 조건으로 걸러지는 비율이 높다면 매매구분을 인덱스에 추가하여 테이블 랜덤 액세스를 줄일 수 있다.
39. 다음은 한 화면에서 반복 호출되는 SQL의 TKPROF 리포트 일부이다. 이에 대한 설명으로 가장 적절하지 않은 것은?
call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 100 0.05 0.06 0 0 0 0 Execute 100 0.02 0.02 0 0 0 0 Fetch 300 1.20 4.85 3600 12400 0 2000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 500 1.27 4.93 3600 12400 0 2000 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS- Parse Call이 Execute와 같은 100회 발생했지만 하드 파싱은 1회이고 나머지는 소프트 파싱이다.
- 1회 수행당 평균 논리적 I/O는 124블록이다.
- 1회 수행당 평균 20건을 반환했으므로 Fetch Call도 1회 수행당 평균 20회 발생했다.
- elapsed와 cpu의 차이(약 3.66초)는 대부분 디스크 I/O 대기 시간으로 추정할 수 있다.
40. 다음은
gather_plan_statistics힌트를 주고 수행한 SQL의DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')결과이다. 이에 대한 설명으로 가장 적절하지 않은 것은?------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 50 | 15620 | 3012 | | 1 | NESTED LOOPS | | 1 | | 50 | 15620 | 3012 | | 2 | NESTED LOOPS | | 1 | 12 | 5000 | 10120 | 3005 | | 3 | TABLE ACCESS BY INDEX ROWID| 주문 | 1 | 12 | 5000 | 5100 | 2990 | |* 4 | INDEX RANGE SCAN | 주문_X01 | 1 | 12 | 5000 | 20 | 15 | |* 5 | INDEX UNIQUE SCAN | 종목_PK | 5000 | 1 | 5000 | 5020 | 15 | |* 6 | TABLE ACCESS BY INDEX ROWID | 종목 | 5000 | 1 | 50 | 5500 | 7 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("O"."주문일자"='20261020') 5 - access("S"."종목코드"="O"."종목코드") 6 - filter(("S"."시장구분"='KOSDAQ' AND "S"."업종코드"='G2510'))- Id 5의 A-Rows 5,000은 Starts 1회당 처리 건수이므로, 전체 처리 건수는 5,000 × 5,000 = 25,000,000건이다.
- 주문 테이블 액세스(Id 3) 자체가 읽은 블록 수는 5,080이다.
- Id 3의 E-Rows 12와 A-Rows 5,000의 큰 차이는 카디널리티 추정 오류를 의미하며, 잘못된 조인 순서·방식 선택의 원인이 될 수 있다.
- 종목 테이블(Id 6)에서 5,000건 중 50건만 남았으므로, 종목 조건의 선택도가 좋다면 종목을 먼저 읽는 조인 순서를 검토할 만하다.
41. Oracle B*Tree 인덱스 구조에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ. 루트에서 어느 리프 블록까지 내려가더라도 거치는 블록 수(높이)가 같다.
- ㄴ. 리프 블록의 각 레코드는 인덱스 키와 ROWID를 가지며 키 순서로 정렬되어 있고, 리프 블록끼리는 양방향 연결 리스트로 이어져 있다.
- ㄷ. 단일 컬럼 B*Tree 인덱스에는 키가 NULL인 행이 저장되지 않으므로,
WHERE 해지일자 IS NULL조건은 해지일자 단일 컬럼 인덱스로 Range Scan할 수 없다. - ㄹ. 테이블 데이터가 10배로 늘면 인덱스 높이도 대략 10배로 늘어나므로 수직적 탐색 비용이 데이터 양에 비례해 커진다.
- ㄱ, ㄴ
- ㄴ, ㄷ
- ㄱ, ㄷ, ㄹ
- ㄱ, ㄴ, ㄷ
42.
주문_X01인덱스(계좌번호)의 일부 구조가 다음과 같다. 계좌번호 'A300'의 주문은 리프 B1의 끝과 B2의 앞에 걸쳐 저장되어 있다.WHERE 계좌번호 = 'A300'을 처리할 때 인덱스 탐색 과정으로 옳은 것은?[루트 블록] (LMC → B1) | A300 → B2 | A500 → B3 | A700 → B4 [리프 B1] A100, A120, … , A290, A300, A300 [리프 B2] A300, A300, A310, … , A480 [리프 B3] A500, … , A690 [리프 B4] A700, … , A990- 루트에서 'A300' 레코드가 가리키는 B2로 내려가 스캔을 시작하고, B2에서 'A310'을 만나면 멈춘다.
- 루트에서 'A300' 바로 앞 레코드(LMC)가 가리키는 B1로 내려가 스캔을 시작하고, B2의 'A310'을 만나면 멈춘다.
- 루트에서 B1로 내려가 스캔을 시작하고, B3의 첫 키 'A500'을 만나면 멈춘다.
- 루트에서 B2로 내려간 뒤 B1 방향으로 역순 스캔하여 'A300'을 모두 찾는다.
43. 다음 인덱스 구성에서 각 SQL의 인덱스 스캔 방식에 대한 설명으로 가장 적절하지 않은 것은? (주문유형은 '01'~'03' 세 가지 값만 가지며, 주문유형·주문일자는 NOT NULL이다.)
CREATE INDEX 주문_X01 ON 주문 (주문유형, 주문일자); CREATE UNIQUE INDEX 체결_PK ON 체결 (주문번호, 체결순번); (가) SELECT * FROM 주문 WHERE 주문일자 = '20261020'; (나) SELECT COUNT(*) FROM 주문; (다) SELECT 주문유형, 주문일자 FROM 주문; (라) SELECT * FROM 체결 WHERE 주문번호 = :ord_no;- (다)를 Index Fast Full Scan으로 처리하면 결과가 인덱스 키 순서대로 나오므로
ORDER BY 주문유형, 주문일자를 추가해도 소트가 생략된다. - (가)는 선두 컬럼 주문유형이 조건에 없지만, 주문유형의 Distinct 값이 적으므로 Index Skip Scan이 효과적일 수 있다.
- (나)는 인덱스에 NOT NULL 컬럼이 있으므로 테이블 대신 더 작은 인덱스를 Index Fast Full Scan으로 읽어 건수를 셀 수 있다.
- (라)는 Unique 결합 인덱스의 일부 컬럼만
=조건으로 주었으므로 Index Unique Scan이 아니라 Index Range Scan으로 처리된다.
- (다)를 Index Fast Full Scan으로 처리하면 결과가 인덱스 키 순서대로 나오므로
44.
주문테이블의 계좌번호와 주문일자는 VARCHAR2 타입이며, 인덱스는주문_X01 (계좌번호, 주문일자)하나뿐이다. 다음 조건 중주문_X01을 Index Range Scan(계좌번호를 액세스 조건으로 사용)할 수 있는 것을 모두 고른 것은? (Oracle 기준)- ㄱ.
WHERE 계좌번호 = 1234567890 - ㄴ.
WHERE 계좌번호 LIKE :acnt || '%' - ㄷ.
WHERE 주문일자 = '20261020' - ㄹ.
WHERE 계좌번호 = :acnt AND SUBSTR(주문일자, 1, 6) = '202610'
- ㄴ
- ㄴ, ㄹ
- ㄱ, ㄴ, ㄹ
- ㄴ, ㄷ, ㄹ
- ㄱ.
45. 인덱스를 통한 테이블 액세스에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ. 클러스터링 팩터가 좋은 인덱스일수록 인덱스 손익분기점이 높아져, 더 많은 비율의 데이터를 읽을 때까지 인덱스가 유리하다.
- ㄴ. 인덱스 ROWID로 테이블 블록을 읽을 때는 그 블록이 버퍼 캐시에 있더라도 해시 체인 탐색과 래치 획득 비용이 든다.
- ㄷ.
주문테이블을주문_X01(계좌번호) 순서로 재정렬해 다시 적재하면주문_X01뿐 아니라주문_X02(종목코드)의 클러스터링 팩터도 함께 좋아진다. - ㄹ. 하루치 주문 전체를 집계하는 배치 프로그램에서는 인덱스보다 Full Table Scan과 해시 조인이 유리한 경우가 많다.
- ㄱ, ㄴ
- ㄴ, ㄹ
- ㄱ, ㄷ, ㄹ
- ㄱ, ㄴ, ㄹ
46. 다음 SQL의 TKPROF Row Source 통계와 Predicate Information이다. 인덱스는
주문_X01 (계좌번호, 주문일자)이다. 가장 효과적인 개선 방안은?SELECT 주문번호, 주문일시, 주문수량, 주문가격 FROM 주문 O WHERE O.계좌번호 = :acnt AND O.종목코드 = :stk AND O.주문일자 >= :from_dt; Rows Row Source Operation ------- --------------------------------------------------- 12 TABLE ACCESS BY INDEX ROWID 주문 (cr=8210 pr=7930 time=6.21 s) 8200 INDEX RANGE SCAN 주문_X01 (cr=25 pr=12 time=0.01 s) Predicate Information (identified by operation id): 1 - filter("O"."종목코드"=:STK) 2 - access("O"."계좌번호"=:ACNT AND "O"."주문일자">=:FROM_DT)- 인덱스를 (계좌번호, 종목코드, 주문일자)로 구성하면 인덱스 스캔 범위와 테이블 랜덤 액세스가 모두 약 12건 수준으로 줄어든다.
- 인덱스를 (계좌번호, 주문일자, 종목코드)로 구성하면 종목코드가 액세스 조건이 되어 인덱스 스캔 범위가 약 12건 수준으로 줄어든다.
- 테이블 랜덤 액세스가 문제이므로
FULL(o)힌트로 Table Full Scan하도록 바꾼다. - 인덱스 스캔 단계의 cr이 대부분이므로
주문_X01인덱스를 리빌드하여 블록 수를 줄인다.
47. 다음 SQL은 하루 수십만 번 수행되는 핵심 조회이다. 종목코드는 약 2,500종류, 매매구분은 '1'(매수)·'2'(매도) 두 가지, 조회 기간은 보통 1주일~1개월이다. 조건 컬럼이 모두 필수 입력일 때, 인덱스 스캔 효율이 가장 좋은 인덱스 구성은?
SELECT 주문번호, 계좌번호, 주문수량, 주문가격 FROM 주문 WHERE 종목코드 = :stk AND 매매구분 = :gb AND 주문일자 BETWEEN :from_dt AND :to_dt;- (주문일자, 종목코드, 매매구분)
- (매매구분, 주문일자, 종목코드)
- (종목코드, 매매구분, 주문일자)
- (종목코드, 주문일자, 매매구분)
48. 인덱스 스캔 효율화에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ. 인덱스가 (주문일자, 종목코드)일 때
주문일자 BETWEEN '20261001' AND '20261031' AND 종목코드 = '005930'으로 조회하면, 종목코드 조건은 스캔 범위를 거의 줄이지 못한다. - ㄴ. ㄱ의 경우 인덱스를 바꿀 수 없다면, 주문일자 조건을 영업일 목록의 IN-List로 바꾸어 일자별로 (주문일자, 종목코드) 범위만 스캔하게 하는 방법을 고려할 수 있다.
- ㄷ. BETWEEN을 IN-List로 바꾸는 방법은 IN-List 값의 개수가 많을수록 항상 더 유리하다.
- ㄹ. 인덱스가 (매매구분, 주문일자)일 때 매매구분이 선택 입력이라
매매구분 LIKE :gb || '%'로 처리하더라도, 값이 입력된 경우의 스캔 효율은매매구분 = :gb와 같다.
- ㄱ, ㄴ, ㄷ
- ㄴ, ㄷ
- ㄱ, ㄹ
- ㄱ, ㄴ
- ㄱ. 인덱스가 (주문일자, 종목코드)일 때
49. 다음은 NL 조인 SQL의 TKPROF Row Source 통계이다. 인덱스는
계좌_X01 (지점코드),주문_X01 (계좌번호, 주문일자)이다. 이에 대한 설명으로 가장 적절한 것은?SELECT /*+ LEADING(A) USE_NL(O) INDEX(A 계좌_X01) INDEX(O 주문_X01) */ A.계좌번호, O.주문번호, O.종목코드, O.주문수량 FROM 계좌 A, 주문 O WHERE A.지점코드 = :brch AND A.계좌유형 = '01' AND O.계좌번호 = A.계좌번호 AND O.주문일자 = :ord_dt; Rows Row Source Operation ------- --------------------------------------------------- 320 NESTED LOOPS (cr=6538 pr=210 time=0.45 s) 2000 TABLE ACCESS BY INDEX ROWID 계좌 (cr=1508 pr=95 time=0.12 s) 3000 INDEX RANGE SCAN 계좌_X01 (cr=8 pr=2 time=0.01 s) 320 TABLE ACCESS BY INDEX ROWID 주문 (cr=5030 pr=115 time=0.31 s) 320 INDEX RANGE SCAN 주문_X01 (cr=4710 pr=40 time=0.22 s)주문_X01인덱스는 계좌_X01에서 읽은 건수와 같은 3,000번 탐색되었다.- 계좌 테이블에서 계좌유형 조건으로 1,000건이 버려졌으며, 계좌유형을
계좌_X01에 추가하면 계좌 테이블 랜덤 액세스를 줄일 수 있다. - 주문 쪽 cr 5,030은 대부분 주문 테이블 블록을 읽은 것이므로 주문 테이블의 클러스터링 팩터를 개선해야 한다.
- 주문 테이블에서 조인 후 필터 조건으로 많은 건이 버려졌으므로 주문 테이블 액세스 단계가 가장 큰 비효율이다.
50. 다음 SQL과 NL 조인에 대한 설명으로 가장 적절하지 않은 것은?
SELECT /*+ LEADING(A) USE_NL(O) */ A.계좌번호, A.고객명, O.주문번호, O.주문수량 FROM 계좌 A, 주문 O WHERE A.지점코드 = :brch AND O.계좌번호 = A.계좌번호 AND O.주문일자 = :ord_dt;- NL 조인은 인덱스를 이용해 정렬 없이 처리하므로, 조인 대상이 수백만 건 이상인 대량 조인에서도 해시 조인보다 항상 유리하다.
- 힌트에 따라 계좌(A)가 Outer(드라이빙), 주문(O)이 Inner인 NL 조인으로 수행된다.
- NL 조인은 부분범위 처리가 가능하므로, 첫 화면에 필요한 건수만 빨리 보여 주는 온라인 조회에 유리하다.
- Inner 쪽 조인 컬럼에 인덱스가 없다면 Outer 건수만큼 주문 테이블을 반복해서 Full Scan하게 되어 매우 비효율적이다.
51. 시장 전체에 영향을 준 이벤트 기간(약 300건)별로 그 기간의 일별시세(약 500만 건)를 조인하려 한다. 두 테이블 모두 조인 컬럼에 인덱스가 없으며 다음 실행계획이 생성되었다. 이에 대한 설명으로 가장 적절하지 않은 것은?
SELECT /*+ LEADING(E) USE_MERGE(S) FULL(E) FULL(S) */ E.이벤트ID, S.종목코드, S.거래일자, S.종가 FROM 시장이벤트 E, 일별시세 S WHERE S.거래일자 BETWEEN E.시작일자 AND E.종료일자; -------------------------------------------------------------- | Id | Operation | Name | Rows | -------------------------------------------------------------- | 0 | SELECT STATEMENT | | 250K | | 1 | MERGE JOIN | | 250K | | 2 | SORT JOIN | | 300 | | 3 | TABLE ACCESS FULL | 시장이벤트 | 300 | |* 4 | FILTER | | | |* 5 | SORT JOIN | | 5000K| | 6 | TABLE ACCESS FULL | 일별시세 | 5000K| -------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("S"."거래일자"<="E"."종료일자") 5 - access("S"."거래일자">="E"."시작일자") filter("S"."거래일자">="E"."시작일자")- 조인 조건에 등치(=) 조건이 없어 해시 조인을 쓸 수 없으므로, 인덱스가 없는 대량 집합 간 조인에서 소트 머지 조인이 현실적인 대안이다.
- Id 5의 SORT JOIN은 일별시세를 조인 키로 정렬해 PGA(부족하면 Temp 테이블스페이스)에 두고, Outer 레코드마다 정렬된 집합에서 시작점을 찾아 스캔한다.
- 머지 단계에서 Inner 집합을 탐색할 때마다 일별시세 테이블 블록을 디스크에서 랜덤 액세스하므로, 거래일자 컬럼에 인덱스가 반드시 있어야 효율적이다.
- 이벤트기간 쪽에 시작일자 인덱스가 있다면 그 인덱스를 이용해 정렬된 순서로 읽음으로써 Id 2의 SORT JOIN을 생략할 수 있다.
52. 다음 SQL과 실행계획에 대한 설명으로 가장 적절한 것은? (종목은 약 2,500건, 조건을 만족하는 주문은 약 240만 건이다.)
SELECT /*+ LEADING(O) USE_HASH(S) SWAP_JOIN_INPUTS(S) FULL(O) FULL(S) */ S.종목명, O.주문번호, O.주문수량 FROM 주문 O, 종목 S WHERE S.종목코드 = O.종목코드 AND O.주문일자 BETWEEN '20261001' AND '20261020'; ------------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------------- | 0 | SELECT STATEMENT | | 2400K | |* 1 | HASH JOIN | | 2400K | | 2 | TABLE ACCESS FULL| 종목 | 2500 | |* 3 | TABLE ACCESS FULL| 주문 | 2400K | -------------------------------------------------------- 종목이 Build Input이 되어 해시 테이블을 만들고 주문이 Probe Input이 된다. 작은 집합을 Build Input으로 써서 해시 테이블이 PGA에 모두 담기면 In-Memory 해시 조인으로 처리된다.
LEADING(O)로 주문을 먼저 지정했으므로 주문이 Build Input이 되고, 종목이 Probe Input이 된다.- 해시 조인은 Inner 쪽 조인 컬럼에 인덱스가 있어야 수행할 수 있으므로 종목_PK 인덱스가 반드시 필요하다.
- 해시 조인은
O.주문가격 BETWEEN S.하한가 AND S.상한가처럼 범위 조건만 있는 조인에도 사용할 수 있다.
53. 계좌(A), 주문(O), 종목(S) 세 테이블을 모두 해시 조인하여 다음 목표 실행계획을 얻으려 한다. 조인 조건은
O.계좌번호 = A.계좌번호,O.종목코드 = S.종목코드뿐이다. 목표 실행계획이 나오는 힌트는?---------------------------------------------------- | 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(S O A) USE_HASH(O) USE_HASH(A)LEADING(O S A) USE_HASH(S) USE_HASH(A) SWAP_JOIN_INPUTS(S)LEADING(A O S) USE_HASH(O) USE_HASH(S)LEADING(O S A) USE_HASH(S) USE_HASH(A) SWAP_JOIN_INPUTS(S) SWAP_JOIN_INPUTS(A)
54. 다음 SQL은 특정 일자의 주문 약 10만 건 중 코스닥 종목 주문만 조회한다. 그날 주문된 종목은 약 800종류이다. 실행계획에 대한 설명으로 가장 적절하지 않은 것은?
SELECT O.주문번호, O.종목코드, O.주문수량 FROM 주문 O WHERE O.주문일자 = '20261020' AND EXISTS (SELECT /*+ NO_UNNEST */ 'X' FROM 종목 S WHERE S.종목코드 = O.종목코드 AND S.시장구분 = 'KOSDAQ'); --------------------------------------------------------- | 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 | --------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "종목" "S" WHERE "S"."종목코드"=:B1 AND "S"."시장구분"='KOSDAQ')) 3 - access("O"."주문일자"='20261020') 4 - filter("S"."시장구분"='KOSDAQ') 5 - access("S"."종목코드"=:B1)- 서브쿼리가 Unnesting되지 않아 FILTER 오퍼레이션으로 처리되었으며, 메인 쿼리 행마다 서브쿼리를 수행하되 입력 값별 결과를 캐싱한다.
- 필터 방식은 NL 세미 조인과 달리 서브쿼리 쪽(종목)을 드라이빙 집합으로 바꿀 수 있어 조인 순서를 유연하게 정할 수 있다.
- 서브쿼리 입력 값(종목코드)의 종류가 약 800개로 적으므로 캐싱 효과가 커서, 서브쿼리 실제 수행 횟수는 10만 번보다 훨씬 적을 수 있다.
- EXISTS 서브쿼리는 조건을 만족하는 첫 행을 찾으면 더 읽지 않고 멈춘다.
55. 다음 SQL의 스칼라 서브쿼리에 대한 설명과, 이를 조인으로 바꿀 때의 고려사항으로 가장 적절하지 않은 것은? (주문 결과는 약 10만 건, 그 안의 종목코드 종류는 수백 개다.)
SELECT O.주문번호, O.주문수량, (SELECT S.종목명 FROM 종목 S WHERE S.종목코드 = O.종목코드) AS 종목명 FROM 주문 O WHERE O.주문일자 = :dt;- 스칼라 서브쿼리는 입력 값(종목코드)과 결과를 캐싱하므로, 입력 값 종류가 적으면 종목 테이블 액세스 횟수가 크게 줄어든다.
- 조인으로 바꿀 때 종목에 매칭되지 않는 주문도 결과에 남겨야 하므로
S.종목코드(+) = O.종목코드처럼 Outer 조인으로 바꿔야 한다. - 스칼라 서브쿼리는 메인 쿼리 건수와 관계없이 종목 테이블을 한 번만 읽어 해시 테이블로 만든 뒤 조인하므로, 대량 데이터에서도 항상 조인보다 빠르다.
- 종목코드가 Unique하지 않아 한 종목코드에 여러 행이 있으면 스칼라 서브쿼리는 오류(ORA-01427)가 나지만, 조인으로 바꾸면 오류 없이 행이 늘어나 결과가 달라진다.
56.
주문테이블의 통계정보가 다음과 같다. 히스토그램은 없으며 모든 컬럼에 NULL은 없다. 다음 SQL에서 옵티마이저가 추정하는 카디널리티(예상 건수)는? (각 조건은 서로 독립이라고 가정하며, 바인드 변수 Peeking은 동작하지 않는다.)항목 값 NUM_ROWS 1,000,000 매매구분 NUM_DISTINCT 2 주문유형 NUM_DISTINCT 5 주문일자 NUM_DISTINCT 1,000 SELECT * FROM 주문 WHERE 매매구분 = :gb AND 주문유형 IN (:t1, :t2) AND 주문일자 BETWEEN :d1 AND :d2;- 500건
- 1,250건
- 2,500건
- 10,000건
57. 옵티마이저와 통계정보에 대한 다음 설명 중 옳은 것을 모두 고른 것은?
- ㄱ. Oracle의 기본 옵티마이저 모드는 ALL_ROWS로, 쿼리 결과 전체를 처리하는 데 드는 비용이 최소인 실행계획을 선택한다.
- ㄴ. FIRST_ROWS_n 모드는 처음 n건을 가장 빨리 반환하는 계획을 고르므로, 결과 전체를 읽어 파일로 내리는 야간 배치에 적합하다.
- ㄷ. 히스토그램은 주문상태처럼 값 분포가 고르지 않은 컬럼에서 리터럴 조건의 카디널리티 추정 정확도를 높인다.
- ㄹ. 클러스터링 팩터가 테이블 블록 수에 가까울수록 인덱스를 통한 테이블 액세스 비용이 낮게 계산된다.
- ㄱ, ㄴ
- ㄴ, ㄷ
- ㄱ, ㄴ, ㄹ
- ㄱ, ㄷ, ㄹ
58. 다음 SQL에 대해 두 가지 실행계획 A, B가 가능하다. 계좌 하나의 당일 주문은 보통 수 건이고, 당일 체결은 전체 종목에 걸쳐 수백만 건이다. 인덱스는
주문_X01 (계좌번호, 주문일자),체결_X01 (체결일자),체결_X02 (종목코드, 체결일자)이다. 이에 대한 설명으로 가장 적절한 것은?SELECT O.주문번호, O.종목코드, V.평균체결가 FROM 주문 O, (SELECT 종목코드, AVG(체결가격) AS 평균체결가 FROM 체결 WHERE 체결일자 = :dt GROUP BY 종목코드) V WHERE O.계좌번호 = :acnt AND O.주문일자 = :dt AND V.종목코드 = O.종목코드; [계획 A] | Id | Operation | Name | | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | | 2 | TABLE ACCESS BY INDEX ROWID | 주문 | |* 3 | INDEX RANGE SCAN | 주문_X01 | | 4 | VIEW | | | 5 | HASH GROUP BY | | | 6 | TABLE ACCESS BY INDEX ROWID | 체결 | |* 7 | INDEX RANGE SCAN | 체결_X01 | [계획 B] | Id | Operation | Name | | 0 | SELECT STATEMENT | | | 1 | HASH GROUP BY | | | 2 | NESTED LOOPS | | | 3 | TABLE ACCESS BY INDEX ROWID | 주문 | |* 4 | INDEX RANGE SCAN | 주문_X01 | | 5 | TABLE ACCESS BY INDEX ROWID | 체결 | |* 6 | INDEX RANGE SCAN | 체결_X02 |- 계획 B는 조건절 Pushing으로 메인 쿼리의 계좌번호 조건을 인라인 뷰 안으로 밀어 넣은 결과이다.
- 계획 B는 인라인 뷰를 Merging하여 조인을 먼저 하고 GROUP BY를 나중에 수행한 것으로, 이 데이터 분포에서는 A보다 B가 유리하다.
- 계획 A를 계획 B로 바꾸려면 인라인 뷰에
NO_MERGE힌트를 지정한다. - 뷰 Merging을 하면 GROUP BY 대상 건수는 Merging하지 않을 때보다 항상 줄어든다.
59. 다음 SQL의 실행계획에서 체결 쪽 인덱스(Id 5)에 SQL에 없던
C.주문번호범위 조건이 액세스 조건으로 나타났다. 이는 어떤 쿼리 변환의 결과인가?SELECT O.주문번호, O.주문수량, C.체결순번, C.체결수량 FROM 주문 O, 체결 C WHERE O.주문번호 = C.주문번호 AND O.주문번호 BETWEEN 1000 AND 2000; ------------------------------------------------------------- | Id | Operation | Name | ------------------------------------------------------------- | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | | 2 | TABLE ACCESS BY INDEX ROWID | 주문 | |* 3 | INDEX RANGE SCAN | 주문_PK | | 4 | TABLE ACCESS BY INDEX ROWID | 체결 | |* 5 | INDEX RANGE SCAN | 체결_PK | ------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("O"."주문번호"="C"."주문번호") 3 - access("O"."주문번호">=1000 AND "O"."주문번호"<=2000) 5 - access("C"."주문번호">=1000 AND "C"."주문번호"<=2000)- 조건절 Pushing (Predicate Pushing)
- 서브쿼리 Unnesting
- 조건절 이행 (Transitive Predicate Generation)
- OR-Expansion
60. 다음 SQL의 힌트에 대한 설명으로 가장 적절한 것은? (Oracle 기준, 인덱스
주문_X01은 존재한다.)SELECT /*+ ORDERED LEADING(S) USE_NL(O) INDEX(주문 주문_X01) */ O.주문번호, S.종목명, O.주문수량 FROM 주문 O, 종목 S WHERE O.계좌번호 = :acnt AND O.주문일자 = :dt AND S.종목코드 = O.종목코드;LEADING(S)가ORDERED보다 우선 적용되므로 종목(S)이 드라이빙 테이블이 된다.- 같은 SQL에 ORDERED와 LEADING을 함께 쓰면 힌트가 상충하므로 SQL 실행 시 문법 오류가 발생한다.
INDEX(주문 주문_X01)은 테이블명으로 지정했지만 같은 테이블을 가리키므로 정상 적용된다.- ORDERED에 따라 FROM 절 순서인 주문 → 종목 순으로 조인하며, INDEX 힌트는 Alias 대신 테이블명을 써서 무시된다.
61.
일별시세테이블의 PK 인덱스는 (종목코드, 거래일자)이다. 다음 중 실행계획에 SORT ORDER BY가 나타나는 SQL은? (옵티마이저는 이 인덱스를 사용한다고 가정한다.)- SELECT * FROM 일별시세 WHERE 종목코드 IN ('005930', '000660') ORDER BY 거래일자;
- SELECT * FROM 일별시세 WHERE 종목코드 = :stk ORDER BY 거래일자 DESC;
- SELECT * FROM 일별시세 WHERE 종목코드 BETWEEN '000000' AND '099999' ORDER BY 종목코드, 거래일자;
- SELECT * FROM 일별시세 WHERE 종목코드 = :stk AND 거래일자 >= :dt ORDER BY 종목코드, 거래일자;
62. 다음 두 SQL과 실행계획에 대한 설명으로 가장 적절하지 않은 것은? 인덱스는
일별시세_PK (종목코드, 거래일자)이다.(가) SELECT MAX(거래일자) FROM 일별시세 WHERE 종목코드 = :stk; | Id | Operation | Name | | 0 | SELECT STATEMENT | | | 1 | SORT AGGREGATE | | | 2 | FIRST ROW | | |* 3 | INDEX RANGE SCAN (MIN/MAX) | 일별시세_PK | (나) SELECT * FROM (SELECT 거래일자, 종가, 거래량 FROM 일별시세 WHERE 종목코드 = :stk ORDER BY 거래일자 DESC) WHERE ROWNUM <= 10; | Id | Operation | Name | | 0 | SELECT STATEMENT | | |* 1 | COUNT STOPKEY | | | 2 | VIEW | | | 3 | TABLE ACCESS BY INDEX ROWID | 일별시세 | |* 4 | INDEX RANGE SCAN DESCENDING | 일별시세_PK |- (가)는 인덱스에서 해당 종목의 가장 큰 거래일자 하나를 찾으면 바로 멈추는 FIRST ROW 방식으로 처리된다.
- (나)는 인덱스를 역순으로 읽어 소트를 생략하고, COUNT STOPKEY에 의해 10건을 읽으면 스캔을 멈춘다.
- (나)의 ROWNUM을 안쪽 뷰에서 RN으로 받고 바깥 쿼리에서
WHERE RN BETWEEN 1 AND 10으로만 거르도록 바꾸어도 COUNT STOPKEY가 동작하므로 성능이 같다. - 인덱스로 소트를 생략할 수 없는 경우에도, ROWNUM 조건이 정렬된 뷰 바로 바깥에 있으면 SORT ORDER BY STOPKEY(Top N 소트)로 상위 N건만 유지하며 정렬하므로 소트 부하가 줄어든다.
63. 거래정지된 종목의 당일 미체결 주문을 일괄 '거부' 처리하려고 다음 UPDATE 문을 작성하였다. 이에 대한 설명으로 가장 적절하지 않은 것은?
UPDATE (SELECT O.주문상태 FROM 주문 O, 종목 S WHERE O.종목코드 = S.종목코드 AND O.주문일자 = :dt AND O.주문상태 = '접수' AND S.거래정지여부 = 'Y') SET 주문상태 = '거부';종목테이블의 종목코드에 PK(또는 Unique) 제약이 있으면 주문은 키 보존 테이블이 되므로, 이 조인 뷰를 통해 주문의 주문상태를 갱신할 수 있다.종목에 PK·Unique 제약이 없더라도 실제 데이터상 종목코드가 중복되지 않는다면 옵티마이저가 이를 확인하여 UPDATE를 허용한다.- 같은 조인 뷰를 통해
종목쪽 컬럼(예: 거래정지여부)을 갱신하려 하면, 종목은 키 보존 테이블이 아니므로 오류가 발생한다. - 같은 처리를 MERGE 문이나
WHERE EXISTS서브쿼리를 쓴 UPDATE로도 작성할 수 있다.
64.
주문테이블은 주문일자(VARCHAR2(8)) 기준으로 2026년 1월~12월 12개 월별 Range 파티션(P01~P12)으로 나뉘어 있다. 다음 세 SQL의 실행계획에 대한 설명으로 가장 적절하지 않은 것은?(가) WHERE 주문일자 BETWEEN '20261001' AND '20261031' | Id | Operation | Name | Pstart| Pstop | | 0 | SELECT STATEMENT | | | | | 1 | PARTITION RANGE SINGLE | | 10 | 10 | |* 2 | TABLE ACCESS FULL | 주문 | 10 | 10 | (나) WHERE 주문일자 BETWEEN :d1 AND :d2 | Id | Operation | Name | Pstart| Pstop | | 0 | SELECT STATEMENT | | | | |* 1 | FILTER | | | | | 2 | PARTITION RANGE ITERATOR| | KEY | KEY | |* 3 | TABLE ACCESS FULL | 주문 | KEY | KEY | 1 - filter(:D1<=:D2) (다) WHERE SUBSTR(주문일자, 1, 6) = '202610' | Id | Operation | Name | Pstart| Pstop | | 0 | SELECT STATEMENT | | | | | 1 | PARTITION RANGE ALL | | 1 | 12 | |* 2 | TABLE ACCESS FULL | 주문 | 1 | 12 |- (나)는 바인드 변수를 사용했으므로 파티션 Pruning이 일어나지 않고 12개 파티션을 모두 읽는다.
- (가)는 Pstart/Pstop이 숫자(10)로 표시되므로 파싱 시점에 읽을 파티션이 결정된 정적 Pruning이다.
- (다)는 파티션 키 컬럼을 SUBSTR로 가공하여 Pruning이 되지 않으므로,
주문일자 BETWEEN '20261001' AND '20261031'처럼 바꿔야 한다. - (나)의 FILTER(Id 1)는
:d1 <= :d2를 먼저 확인하여, 조건이 거짓이면 테이블을 읽지 않기 위한 것이다.
65. 다음은 DOP 4로 병렬 수행한 조인 SQL의 실행계획이다. 계좌와 주문은 모두 파티셔닝되어 있지 않다. 이에 대한 설명으로 가장 적절하지 않은 것은?
SELECT /*+ LEADING(A) USE_HASH(O) FULL(A) FULL(O) PARALLEL(A 4) PARALLEL(O 4) PQ_DISTRIBUTE(O HASH HASH) */ A.지점코드, SUM(O.주문수량) FROM 계좌 A, 주문 O WHERE O.계좌번호 = A.계좌번호 GROUP BY A.지점코드; ---------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10003 | Q1,03 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,03 | PCWP | | | 4 | PX RECEIVE | | Q1,03 | PCWP | | | 5 | PX SEND HASH | :TQ10002 | Q1,02 | P->P | HASH | |* 6 | HASH JOIN BUFFERED | | Q1,02 | PCWP | | | 7 | PX RECEIVE | | Q1,02 | PCWP | | | 8 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 9 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 10 | TABLE ACCESS FULL | 계좌 | Q1,00 | PCWP | | | 11 | PX RECEIVE | | Q1,02 | PCWP | | | 12 | PX SEND HASH | :TQ10001 | Q1,01 | P->P | HASH | | 13 | PX BLOCK ITERATOR | | Q1,01 | PCWC | | | 14 | TABLE ACCESS FULL | 주문 | Q1,01 | PCWP | | ----------------------------------------------------------------------------- 두 테이블 모두 조인 키의 해시 값으로 재분배되었으며,
PQ_DISTRIBUTE(O HASH HASH)로 지정한 분배 방식에 해당한다. - 서버 집합 사이에 P->P 재분배가 있으므로 생산자와 소비자 두 서버 집합, 즉 DOP의 2배인 8개 병렬 서버가 사용된다.
- 계좌 테이블이 매우 작다면 계좌를 모든 병렬 서버에 복제하는 BROADCAST 분배가 더 효율적일 수 있다.
- 두 테이블이 조인 키로 같은 방식으로 파티셔닝된 것과 같은 효과를 얻는 Full Partition-Wise 조인이므로 재분배 비용이 없다.
- 두 테이블 모두 조인 키의 해시 값으로 재분배되었으며,
66. 다음 원본 SQL은 체결 테이블을 스칼라 서브쿼리로 두 번 읽는다. 체결을 한 번만 읽으면서 원본과 항상 같은 결과를 내도록 바꾼 SQL은? (계좌번호는 계좌의 PK이다.)
-- 원본 SELECT A.계좌번호, (SELECT SUM(C.체결금액) FROM 체결 C WHERE C.계좌번호 = A.계좌번호 AND C.체결일자 = :dt AND C.매매구분 = '1') AS 매수금액, (SELECT SUM(C.체결금액) FROM 체결 C WHERE C.계좌번호 = A.계좌번호 AND C.체결일자 = :dt AND C.매매구분 = '2') AS 매도금액 FROM 계좌 A WHERE A.지점코드 = :brch; -- (가) SELECT A.계좌번호, SUM(CASE WHEN C.매매구분 = '1' THEN C.체결금액 END) AS 매수금액, SUM(CASE WHEN C.매매구분 = '2' THEN C.체결금액 END) AS 매도금액 FROM 계좌 A, 체결 C WHERE A.지점코드 = :brch AND C.계좌번호 = A.계좌번호 AND C.체결일자 = :dt GROUP BY A.계좌번호; -- (나) SELECT A.계좌번호, SUM(CASE WHEN C.매매구분 = '1' THEN C.체결금액 END) AS 매수금액, SUM(CASE WHEN C.매매구분 = '2' THEN C.체결금액 END) AS 매도금액 FROM 계좌 A, 체결 C WHERE A.지점코드 = :brch AND C.계좌번호(+) = A.계좌번호 AND C.체결일자 = :dt GROUP BY A.계좌번호; -- (다) SELECT A.계좌번호, SUM(CASE WHEN C.매매구분 = '1' THEN C.체결금액 END) AS 매수금액, SUM(CASE WHEN C.매매구분 = '2' THEN C.체결금액 END) AS 매도금액 FROM 계좌 A, 체결 C WHERE A.지점코드 = :brch AND C.계좌번호(+) = A.계좌번호 AND C.체결일자(+) = :dt GROUP BY A.계좌번호; -- (라) SELECT A.계좌번호, SUM(DECODE(C.매매구분, '1', C.체결금액, 0)) AS 매수금액, SUM(DECODE(C.매매구분, '2', C.체결금액, 0)) AS 매도금액 FROM 계좌 A, 체결 C WHERE A.지점코드 = :brch AND C.계좌번호(+) = A.계좌번호 AND C.체결일자(+) = :dt GROUP BY A.계좌번호;- (가)
- (나)
- (다)
- (라)
67. 세션 1이 다음 UPDATE를 수행하고 아직 커밋하지 않았다. 이때 세션 2에서 실행했을 때 대기(블로킹)가 발생하는 문장은? (Oracle 11g 이상 기준)
-- 세션 1 (미커밋) UPDATE 주문 SET 주문상태 = '정정' WHERE 주문번호 = 1001;- LOCK TABLE 주문 IN SHARE MODE;
- UPDATE 주문 SET 주문상태 = '취소' WHERE 주문번호 = 1002;
- SELECT 주문상태 FROM 주문 WHERE 주문번호 = 1001;
- INSERT INTO 주문 (주문번호, 계좌번호, 종목코드, 주문수량) VALUES (9999, 'A1', '005930', 10);
68. 두 세션이 다음 순서로 문장을 실행하였다. 이에 대한 설명으로 가장 적절하지 않은 것은? (Oracle 기준)
시점 세션 1 세션 2 ---- ------------------------------------------ ------------------------------------------ t1 UPDATE 계좌 SET 예수금 = 예수금 - 100 WHERE 계좌번호 = 'A1'; t2 UPDATE 계좌 SET 예수금 = 예수금 + 100 WHERE 계좌번호 = 'A2'; t3 UPDATE 계좌 SET 예수금 = 예수금 + 100 WHERE 계좌번호 = 'A2'; -- 대기 t4 UPDATE 계좌 SET 예수금 = 예수금 - 100 WHERE 계좌번호 = 'A1';- t4 시점에 두 세션이 서로의 Lock을 기다리는 교착상태가 되며, Oracle은 이를 감지해 한쪽 세션에 ORA-00060 오류를 발생시킨다.
- 오류를 받은 세션은 교착상태를 일으킨 문장만 롤백되고, 그 이전에 수행한 UPDATE의 Lock은 여전히 보유한다.
- 모든 프로그램이 계좌번호 오름차순처럼 같은 순서로 행을 갱신하도록 하면 이와 같은 교착상태를 예방할 수 있다.
- 오류를 받은 세션의 트랜잭션 전체가 자동으로 롤백되므로, 다른 세션은 즉시 대기에서 풀려 작업을 계속할 수 있다.
69. 두 트랜잭션이 Oracle 기본 격리성 수준에서 다음 순서로 실행되었다. t3 시점 TX1의 조회 결과와 이 현상에 대한 설명으로 옳은 것은?
시점 TX1 TX2 ---- -------------------------------------------- --------------------------------------- t1 SELECT 예수금 FROM 계좌 WHERE 계좌번호 = 'A1'; -- 1,000 t2 UPDATE 계좌 SET 예수금 = 500 WHERE 계좌번호 = 'A1'; COMMIT; t3 SELECT 예수금 FROM 계좌 WHERE 계좌번호 = 'A1'; -- ?- 1,000 — Oracle은 문장수준 읽기 일관성을 보장하므로 TX1 안에서는 항상 처음 읽은 값을 다시 읽는다.
- 500 — Non-Repeatable Read가 발생한 것이며, TX1이 SERIALIZABLE 수준이었다면 t3에서도 1,000을 읽는다.
- 500 — Phantom Read가 발생한 것이며, TX1이 SERIALIZABLE 수준이었다면 t3에서도 1,000을 읽는다.
- 500 — Non-Repeatable Read가 발생한 것이며, TX1이 SERIALIZABLE 수준이었다면 TX2의 UPDATE가 TX1 종료까지 대기한다.
70. 다음은 계좌 예수금 변경 화면의 처리 로직이다. 이에 대한 설명으로 가장 적절하지 않은 것은?
-- (1) 조회 SELECT 예수금, 변경일시 INTO :bal, :mod_ts FROM 계좌 WHERE 계좌번호 = :acnt; -- (2) 사용자가 화면에서 값을 수정 (수 초 ~ 수 분) -- (3) 저장 UPDATE 계좌 SET 예수금 = :new_bal, 변경일시 = SYSTIMESTAMP WHERE 계좌번호 = :acnt AND 변경일시 = :mod_ts; -- (4) SQL%ROWCOUNT = 0 이면 '다른 사용자가 먼저 변경했습니다' 안내 후 재조회- SELECT 시점에 해당 행에 로우 Lock이 걸리므로, 사용자가 화면에서 값을 고치는 동안 다른 트랜잭션은 이 계좌를 UPDATE하지 못하고 대기한다.
- 읽을 때 Lock을 걸지 않고 변경 시점에 다른 트랜잭션의 변경 여부를 확인하는 낙관적 동시성 제어 방식이다.
- UPDATE 결과가 0건이면 그 사이 다른 트랜잭션이 먼저 변경한 것이므로 다시 조회한 뒤 재처리하도록 안내한다.
- 이 테이블을 갱신하는 다른 프로그램이 변경일시를 갱신하지 않는다면, 그 프로그램의 변경은 충돌로 감지되지 않아 갱신 손실이 생길 수 있다.
AI가 SQLP 출제 범위를 바탕으로 새로 만든 예상 문제이며, 실제 기출문제가 아닙니다.