SQLP 21회 2016.06.04

자격증/SQLP - 기출복원

2020. 11. 11. 09:21

출처 : cafe.naver.com/sqlpd/5323

 

------------------------------------------------------------------------------------------------

1. 아래의 테이블 정보와 요구조건을 만족하는 SQL 을 작성하시오

   단, 조회/다음 버튼과 전체 버튼(파일 내려받기) 에 대한 SQL 은 각각 작성하고 필요한 경우 

   INDEX 를 생성하시오

------------------------------------------------------------------------------------------------

1) TABLE 정보

 

고객 : 100,000 건

--------------

#고객번호 (PK)

*고객명

*등록일자

*고객상태코드 ('AC' 건수 : 10,000 건)

*연락처

 

 

고객접속이력

- 10,000,000건

- 접속일자로 월별 파티션

--------------

#접속일자 (PK1)

#고객번호 (PK2)

*기타1

 

 

2) 전제조건 

 

 - 조회 정보 : 고객번호, 고객명 , 연락처, 등록일자, 최근접속일자

 - 조회 요건

   + 최근접속일자 : 한 달 이내 접속일자 중 가장 최근접속일자

                    -> TRUNC(ADD_MONTHS(SYSDATE, -1)

   + 조회/다음 버튼 시 한 페이지 출력 건수 : 20건

   + 전체 버튼 시 출력 건수 : 모든 데이터

   + 정렬 : 등록일자, 고객번호 순으로 정렬하여 출력

   + 조회/다음 버튼 시 빠른 응답속도 중요

   

 - 기타 조건

   + 인덱스가 삭제되어도 결과 집합에 영향이 없어야 함

   + VIEW MERGE, UNNEST, JOIN PUSH PREDICATE 쿼리변환이 불가능한 DBMS 버전임

 

 

 

**** 예시 답안 ****

 

1. 문제 의도

   - 페이지 처리를 통한 TOP N 알고리즘을 이용한 SQL 작성

   - 인덱스 삭제 시 결과 정합성 유지가 전제조건이므로 INDEX 힌트를 통한 ROWNUM=1 은 원천 차단

   - 고객상태코드가 전체 데이터의 10% 정도로 단일 조건 인덱스 스캔과 FULL SCAN 의 경계는 모호함

   - TOP N 알고리즘을 이용하기 위한 인덱스 생성

   - 빠른 응답속도를 요구한 것으로 Nested loop join 을 통한 부분범위 처리를 기대

   - 전체 버튼은 전체 데이터 처리이므로 Hash Join 을 통한 전체 처리를 기대

   - 전체 조회 시 고객접속이력이 월별 파티션으로 되어있어 접속일자를 통한 인덱스 처리가 아닌 Full Scan 처리 필요

 

2. TOP N 알고리즘 --> 맨 뒤 Test 에서 설명

 

3. 예시 답안

 

3.1 조회/다음 버튼 SQL

 

1) 다음 버튼이 빈번히 사용되지 않을 경우

 

- INDEX 생성

 

CREATE INDEX IX_고객_01 ON 고객(고객상태코드, 등록일자, 고객번호);

CREATE INDEX IX_고객접속이력_01 ON 고객접속이력(고객번호,접속일자);

 

 

- 조회 버튼 클릭 시 next_no 는 1 을 던져주고, 이후 다음 버튼 클릭시 이전 값에 +1 을 넣어 던져줌

 

SELECT /*+ USE_NL(B) */

       A.고객번호, A.고객명, A.연락처, A.등록일자,

       MAX(B.접속일자) AS 최근접속일자       

FROM 

      ( SELECT ROWNUM RN, X.*

        FROM (

               SELECT 고객번호, 고객명, 연락처, 등록일자 

               FROM 고객

               WHERE 1=1

               AND 고객상태코드 = 'AC'

               ORDER BY 등록일자, 고객번호

               ) X

        WHERE ROWNUM  <= :next_no * 20

      ) A,

      고객접속이력 B

WHERE 1=1

AND A.고객번호 = B.고객번호(+)

AND B.접속일자(+) >= TRUNC(ADD_MONTHS(SYSDATE,-1)) 

AND A.RN BETWEEN (:next_no-1)*20 +1  AND :next_no*20 

GROUP BY A.고객번호, A.고객명, A.연락처, A.등록일자

ORDER BY A.등록일자, A.고객번호;

 

 

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Parse        1    0.000        0.001          0          0          0          0

Execute      1    0.000        0.005          0          0          0          0

Fetch        2    0.016        0.004         10         47          0         20

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Total        4    0.016        0.010         10         47          0         20

 

Misses in library cache during parse   : 1

Misses in library cache during execute : 1

Optimizer Goal : ALL_ROWS

Parsing user : LSS (ID=91)

 

 

Rows     Row Source Operation

-------  -----------------------------------------------------------------------

     20  SORT GROUP BY (cr=47 pr=10 pw=0 time=0 us cost=24000 size=11164980 card=105330)

    200   FILTER  (cr=47 pr=10 pw=0 time=199 us)

    200    NESTED LOOPS OUTER (cr=47 pr=10 pw=0 time=199 us cost=21464 size=11164980 card=105330)

     20     VIEW  (cr=4 pr=0 pw=0 time=266 us cost=173 size=968149 card=10639)

     20      COUNT STOPKEY (cr=4 pr=0 pw=0 time=0 us)

     20       VIEW  (cr=4 pr=0 pw=0 time=0 us cost=173 size=829842 card=10639)

     20        TABLE ACCESS BY INDEX ROWID 같같 (cr=4 pr=0 pw=0 time=0 us cost=173 size=861759 card=10639)

     20         INDEX RANGE SCAN IX_같같_01 (cr=3 pr=0 pw=0 time=38 us cost=46 size=0 card=10639)

    200     INDEX RANGE SCAN IX_같같좋세응렁_01 (cr=43 pr=10 pw=0 time=42 us cost=2 size=150 card=10)

 

 

 

2) 다음 버튼이 빈번히 사용될 경우

 

- INDEX 생성

 

CREATE INDEX IX_고객_01 ON 고객(고객상태코드, 등록일자, 고객번호);

CREATE INDEX IX_고객접속이력_01 ON 고객접속이력(고객번호,접속일자);

 

 

- 조회 버튼 클릭 시 이전등록일자와 이전고객번호는 NULL 값을 던져주고, 다음버튼 클릭 시 조회 버튼 클릭간 나온

  가장 나중 값을 파라미터로 던져줌

 

 

SELECT /*+ LEADING(A) USE_NL(B) */

       A.고객번호, A.고객명, A.연락처, A.등록일자,

       MAX(B.접속일자) AS 최근접속일자       

FROM 

      ( SELECT ROWNUM RN, X.*

        FROM (

               SELECT 고객번호, 고객명, 연락처, 등록일자 

               FROM 고객

               WHERE 1=1

               AND 고객상태코드 = 'AC'

               AND 등록일자 >= NVL(TO_DATE(:이전등록일자,'YYYYMMDD HH24:MI:SS'), 등록일자-1)

               AND 고객번호 > NVL(:이전고객번호, ' ')  -- 스페이스 한칸 (모든 조건 만족)

               ORDER BY 등록일자, 고객번호

               ) X

        WHERE ROWNUM  <= 20

      ) A,

      고객접속이력 B

WHERE 1=1

AND A.고객번호 = B.고객번호(+)

AND B.접속일자(+) >= TRUNC(ADD_MONTHS(SYSDATE,-1)) 

GROUP BY A.고객번호, A.고객명, A.연락처, A.등록일자

ORDER BY A.등록일자, A.고객번호;

 

** 등록일자의 경우 이전등록일자와 동일한 날짜일수도 있으므로 같다로 표시하고

   대신 고객번호는 항상 크다로 인식하여 맨 마지막 줄이 중복출력되는 것을 방지함

 

 

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Parse        1    0.000        0.001          0          0          0          0

Execute      1    0.016        0.008          0          0          0          0

Fetch        2    0.000        0.001          0         47          0         20

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Total        4    0.016        0.009          0         47          0         20

 

Misses in library cache during parse   : 1

Misses in library cache during execute : 1

Optimizer Goal : ALL_ROWS

Parsing user : LSS (ID=91)

 

 

Rows     Row Source Operation

-------  -----------------------------------------------------------------------

     20  SORT GROUP BY (cr=47 pr=0 pw=0 time=0 us cost=46 size=18414 card=198)

    200   NESTED LOOPS OUTER (cr=47 pr=0 pw=0 time=199 us cost=45 size=18414 card=198)

     20    VIEW  (cr=4 pr=0 pw=0 time=133 us cost=5 size=1560 card=20)

     20     COUNT STOPKEY (cr=4 pr=0 pw=0 time=95 us)

     20      VIEW  (cr=4 pr=0 pw=0 time=0 us cost=5 size=1638 card=21)

     20       TABLE ACCESS BY INDEX ROWID 같같 (cr=4 pr=0 pw=0 time=0 us cost=5 size=1701 card=21)

     20        INDEX RANGE SCAN IX_같같_01 (cr=3 pr=0 pw=0 time=38 us cost=4 size=0 card=505)

    200    INDEX RANGE SCAN IX_같같좋세응렁_01 (cr=43 pr=0 pw=0 time=94 us cost=2 size=150 card=10)

 

 

3.2 전체 버튼 SQL

 

- 월별 파티션으로 FULL SCAN 이 유리함(인덱스 보다)

 

SELECT /*+ LEADING(A) USE_HASH(B) FULL(B) */

       A.고객번호, A.고객명, A.연락처, A.등록일자,

       MAX(B.접속일자) AS 최근접속일자       

FROM 고객 A,

     고객접속이력 B

WHERE 1=1

AND A.고객번호 = B.고객번호(+)

AND A.고객상태코드 = 'AC'

AND B.접속일자(+) >= TRUNC(ADD_MONTHS(SYSDATE,-1)) 

GROUP BY A.고객번호, A.고객명, A.연락처, A.등록일자

ORDER BY A.등록일자, A.고객번호;

 

 

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Parse        1    0.000        0.003          0          0          0          0

Execute      1    0.000        0.000          0          0          0          0

Fetch      101    0.406        0.402       4057       5033          0      10000

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Total      103    0.406        0.405       4057       5033          0      10000

 

Misses in library cache during parse   : 1

Optimizer Goal : ALL_ROWS

Parsing user : LSS (ID=91)

 

 

Rows     Row Source Operation

-------  -----------------------------------------------------------------------

  10000  SORT GROUP BY (cr=5033 pr=4057 pw=0 time=3589 us cost=14642 size=10111584 card=105329)

 100000   HASH JOIN OUTER (cr=5033 pr=4057 pw=0 time=4483842 us cost=12324 size=10111584 card=105329)

  10000    TABLE ACCESS BY INDEX ROWID 같같 (cr=164 pr=0 pw=0 time=6075 us cost=173 size=861759 card=10639)

  10000     INDEX RANGE SCAN IX_같같_01 (cr=43 pr=0 pw=0 time=2531 us cost=46 size=0 card=10639)

1000000    PARTITION RANGE ITERATOR PARTITION: KEY 12 (cr=4869 pr=4057 pw=0 time=1642389 us cost=12143 size=14864505 card=990967)

1000000     TABLE ACCESS FULL 같같좋세응렁 PARTITION: KEY 12 (cr=4869 pr=4057 pw=0 time=94457 us cost=12143 size=14864505 card=990967)

 

 

------------------------------------------------------------------------------------------------

2. 아래의 SQL 를 보고 최적의 성능을 내기 위해 비어있는 Hint 절에 알맞은 Hint 를 적으시오

   필요 시 인덱스를 구성하고, 인덱스와 Hint 를 통해 만들어진 최적의 실행계획을 보기를 참고하여

   작성하시오

------------------------------------------------------------------------------------------------

 

1) SQL

SELECT /*+ (                  ) */ A.고객번호, A.고객명,A.연락처, A.등록일자

FROM 고객 A

WHERE A.고객상태코드 = 'AC'

AND EXISTS (SELECT /*+ (             ) */ 'X'

            FROM 고객상담이력 B

            WHERE B.고객번호 = A.고객번호

            AND B.접속일자 <= TRUNC(ADD_MONTHS(SYSDATE, -1))

           )

AND 등록일자 >= TRUNC(ADD_MONTHS(SYSDATE, -1))

ORDER BY A.등록일자;

 

 

2) TABLE 정보

 

고객 : 100,000 건

--------------

#고객번호 (PK)

*고객명

*등록일자 (등록일자가 한 달이내인 사람은 고객상태코드가 'AC' 인 경우 보통 50% 정도임)

*고객상태코드 ('AC' 건수 : 10,000 건, 그 중 50%가 한 달이내 접속)

*연락처

 

 

고객상담이력

- 10,000,000건

--------------

#고객번호 (PK1)

#접속일자 (PK2)

*상담내용

 

 

3) 전제조건 

 

 - 조회 정보 : 고객번호, 고객명 , 연락처, 등록일자

 - 조회 요건

   + 빠른 응답속도 중요

   + ?

   

   

4) SQL OPERATION 보기 

 

SORT (ORDER BY)

 

NESTED LOOPS (SEMI)

HASH JOIN (SEMI)

 

FILTER

 

INDEX (UNIQUE SCAN) OF (index_name) (INDEX (UNIQUE))

INDEX (RANGE SCAN) OF (index_name) (INDEX)

INDEX (FULL SCAN) OF (index_name) (INDEX)

 

TABLE ACCESS (BY INDEX ROWID) OF (table_name) (TABLE)

TABLE ACCESS (FULL) OF (table_name) (TABLE)

 

**** 예시 답안 ****

 

1. 문제 의도

   - 'AC' 코드로 도출되는 데이터는 10,000건으로 전체 데이터의 10% 선으로 INDEX 를 통한 SCAN 과 

      FULL SCAN 의 경계가 모호하나 'AC' 인 고객의 경우 등록일자가 한달 이내인 사람은 50% 정도로 

      10,000건의 50% 인 5,000건 정도임

      * 전체 5% 정도로 인덱스 스캔이 유리하므로 인덱스 사용 힌트가 나와야 함

      

   - EXSITS 에 대한 FILTER 처리를 하느냐, SEMI NESTED LOOPS JOIN 또는 SEMI HASH JOIN 을 결정해야 함

      * 일반적으로 FILTER 처리 보다는 SEMI HASH JOIN 의 성능이 좋으며 건수가 적을 경우는 SEMI NESTED LOOPS JOIN 이 나음

   

   - 서브쿼리 내에 사용되는 고객상담이력에 대한 적절한 인덱스 구성을 하였는가?

      * 서브쿼리가 확인자 역할을 해야 하기 때문에 적절한 인덱스가 필요함 (SEMI JOIN 경우등)

   

   - 등록일자로 ORDER BY 를 하고 있으나 INDEX 구성에 고객상담코드 + 등록일자로 구성할 경우는 SORT ORDER BY 가 나타나지 않음

   

 

2. 예시 답안

 

1) 인덱스 예시 답안

 

고객 테이블 인덱스 생성

 - IX_고객_01 : 고객상태코드 + 등록일자  

 

고객상담이력 테이블 기존 인덱스 사용 (없을 경우 생성)

 - PK_고객상담이력 (고객번호 , 접속일자) 사용 

 

2) Hint 예시 답안

 

SELECT /*+ INDEX(A IX_고객_01) */ A.고객번호, A.고객명,A.연락처, A.등록일자

FROM 고객 A

WHERE A.고객상태코드 = 'AC'

AND EXISTS (SELECT /*+ UNNEST NL_SJ INDEX(B PK_고객상담이력) */ 'X'

            FROM 고객상담이력 B

            WHERE B.고객번호 = A.고객번호

            AND B.접속일자 <= TRUNC(ADD_MONTHS(SYSDATE, -1))

           )

AND A.등록일자 >= TRUNC(ADD_MONTHS(SYSDATE, -1))

ORDER BY A.등록일자;

 

*** UNNEST 는 반드시는 아님. 일반적으로 NL_SJ 등과 같이 쓰이며, FILTER 로 풀고 싶을 때

    NO_UNNEST 사용 가능함 

 

3) PLAN 예시 답안

 

SORT (ORDER BY) -> 고객상태코드 + 등록일자라 나타나지 않을 수 있음(실제로 Oracle 에서 안 나타남)

    NESTED LOOPS (SEMI)

        TABLE ACCESS (BY INDEX ROWID) OF 고객 (TABLE)

            INDEX (RANGE SCAN) OF IX_고객_01 (INDEX)

        INDEX (RANGE SCAN) OF PK_고객상담이력 (INDEX)

 

 

 

*** 실제 플랜

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=18K Card=9K Bytes=817K)

   1    0   NESTED LOOPS (SEMI) (Cost=18K Card=9K Bytes=817K)

   2    1     TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=132 Card=9K Bytes=689K)

   3    2       INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=28 Card=9K)

   4    1     INDEX (RANGE SCAN) OF 'PK_고객상담이력' (INDEX) (Cost=2 Card=9M Bytes=128M)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   3 - access("A"."고객상태코드"='AC' AND "A"."등록일자">=TRUNC(ADD_MONTHS(SYSDATE@!,(-1))) AND "A"."등록일자" IS NOT NULL)

   4 - access("B"."고객번호"="A"."고객번호" AND "B"."접속일자"<=TRUNC(ADD_MONTHS(SYSDATE@!,(-1))))

-----------------------------------------------------------

 

-- TRACE

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Parse        1    0.000        0.002          0          0          0          0

Execute      1    0.000        0.000          0          0          0          0

Fetch       26    0.031        0.016          0      10153          0       2499

------- ------ -------- ------------ ---------- ---------- ---------- ----------

Total       28    0.031        0.018          0      10153          0       2499

 

Misses in library cache during parse   : 1

Optimizer Goal : ALL_ROWS

Parsing user : LSS (ID=91)

 

 

Rows     Row Source Operation

-------  -----------------------------------------------------------------------

   2499  NESTED LOOPS SEMI (cr=10153 pr=0 pw=0 time=7231 us cost=17562 size=836160 card=8710)

   5000   TABLE ACCESS BY INDEX ROWID 고객 (cr=127 pr=0 pw=0 time=4874 us cost=132 size=705510 card=8710)

   5000    INDEX RANGE SCAN IX_고객_01 (cr=42 pr=0 pw=0 time=2249 us cost=28 size=0 card=8710)

   2499   INDEX RANGE SCAN PK_고객상담이력 (cr=10026 pr=0 pw=0 time=0 us cost=2 size=134189775 card=8945985)

   

 

 

* Top N 알고리즘 Test 자료

 

1. TOP N 알고리즘

   - COUNT STOPKEY 를 통해 전체를 읽지 않고 INDEX 를 통한 SORT 후 필요부분 까지 STOP 함

   - ORDER BY 바같의 SELECT 절에서 ROWNUM 을 구하고 조건절로 ROWNUM <= :STOPCNT 를 사용할 경우 TOP N 알고리즘이 작동함 (COUNT(STOPKEY)) (예제1)

   - ORDER BY 안쪽의 SELECT 절에서 ROWNUM 를 구하고 해당 ROWNUM 의 ALIAS 를 통해 조건을 사용 할 경우 STOPKEY 가 되지 못함 (예제 2)

   - ORDER BY 안쪽의 SELECT 절에서 ROWNUM 을 구하고 해당 ROWNUM 의 ALIAS 를 사용하지 않고 ROWNUM <= :STOPCNT 를 사용할 경우 TOP N 알고리즘 작동 (예제3)

   - ORDER BY 대신 WINDOW FUNCTION [ROW_NUMBER] 를 사용하여 해당 ALIAS 를 통해 상수 조건을 사용할 경우 TOP N 알고리즘이 작동함 (예제4)

   - ORDER BY 대신 WINDOW FUNCTION [ROW_NUMBER] 를 사용하여 해당 ALIAS 를 통해 바인드 조건을 사용할 경우 TOP N 알고리즘이 작동하지 않음 (예제5)

   - ORDER BY 대신 WINDOW FUNCTION [ROW_NUMBER] 를 사용하고 ROWNUM <= :STOPCNT 바인드 조건을 사용할 경우 TOP N 알고리즘이 작동하나 ORDER BY 가 없으므로 정렬 안됨

 

   * TOP N 알고리즘 작동시 Buffer Get : 6

     TOP N 알고리즘 미작동시 Buffer Get : 166 

 

* 예제 테스트 전 INDEX 생성

CREATE INDEX IX_고객_01 ON 고객(고객상태코드, 등록일자, 고객번호);

 

예제1)

 

SELECT ROWNUM RN, X.*

FROM (

       SELECT 고객번호, 고객명, 연락처, 등록일자 

       FROM 고객

       WHERE 1=1

       AND 고객상태코드 = 'AC'

       ORDER BY 등록일자, 고객번호

       ) X

WHERE ROWNUM  <= :next_no * 20;

 

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=173 Card=11K Bytes=499K)

   1    0   COUNT (STOPKEY)

   2    1     VIEW (Cost=173 Card=11K Bytes=499K)

   3    2       TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=173 Card=11K Bytes=842K)

   4    3         INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=46 Card=11K)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   1 - filter(ROWNUM<=TO_NUMBER(:NEXT_NO)*20)

   4 - access("고객상태코드"='AC')

-----------------------------------------------------------

 

예제 2)

 

SELECT X.*

FROM (

       SELECT ROWNUM RN,

              고객번호, 고객명, 연락처, 등록일자 

       FROM 고객

       WHERE 1=1

       AND 고객상태코드 = 'AC'

       ORDER BY 등록일자, 고객번호

       ) X

WHERE RN  <= :next_no * 20;

 

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=173 Card=11K Bytes=634K)

   1    0   VIEW (Cost=173 Card=11K Bytes=634K)

   2    1     COUNT

   3    2       TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=173 Card=11K Bytes=842K)

   4    3         INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=46 Card=11K)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   1 - filter("RN"<=TO_NUMBER(:NEXT_NO)*20)

   4 - access("고객상태코드"='AC')

-----------------------------------------------------------

 

 

예제 3)

 

SELECT X.*

FROM (

       SELECT ROWNUM RN,

              고객번호, 고객명, 연락처, 등록일자 

       FROM 고객

       WHERE 1=1

       AND 고객상태코드 = 'AC'

       ORDER BY 등록일자, 고객번호

       ) X

WHERE ROWNUM  <= :next_no * 20;

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=173 Card=11K Bytes=634K)

   1    0   COUNT (STOPKEY)

   2    1     VIEW (Cost=173 Card=11K Bytes=634K)

   3    2       COUNT

   4    3         TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=173 Card=11K Bytes=842K)

   5    4           INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=46 Card=11K)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   1 - filter(ROWNUM<=TO_NUMBER(:NEXT_NO)*20)

   5 - access("고객상태코드"='AC')

-----------------------------------------------------------

 

 

예제4)

 

SELECT X.*

FROM (

       SELECT ROW_NUMBER() OVER(ORDER BY 등록일자,고객번호) RN,

              고객번호, 고객명, 연락처, 등록일자 

       FROM 고객

       WHERE 1=1

       AND 고객상태코드 = 'AC'

       ) X

WHERE RN  <= 20;

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=378 Card=11K Bytes=634K)

   1    0   VIEW (Cost=378 Card=11K Bytes=634K)

   2    1     WINDOW (NOSORT STOPKEY) (Cost=378 Card=11K Bytes=842K)

   3    2       TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=173 Card=11K Bytes=842K)

   4    3         INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=46 Card=11K)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   1 - filter("RN"<=20)

   2 - filter(ROW_NUMBER() OVER ( ORDER BY "등록일자","고객번호")<=20)

   4 - access("고객상태코드"='AC')

-----------------------------------------------------------

 

 

예제 5)

 

SELECT X.*

FROM (

       SELECT ROW_NUMBER() OVER(ORDER BY 등록일자,고객번호) RN,

              고객번호, 고객명, 연락처, 등록일자 

       FROM 고객

       WHERE 1=1

       AND 고객상태코드 = 'AC'

       ) X

WHERE RN  <= :next_no * 20;

 

Execution Plan

-----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=378 Card=11K Bytes=634K)

   1    0   VIEW (Cost=378 Card=11K Bytes=634K)

   2    1     WINDOW (NOSORT) (Cost=378 Card=11K Bytes=842K)

   3    2       TABLE ACCESS (BY INDEX ROWID) OF '고객' (TABLE) (Cost=173 Card=11K Bytes=842K)

   4    3         INDEX (RANGE SCAN) OF 'IX_고객_01' (INDEX) (Cost=46 Card=11K)

-----------------------------------------------------------

 

Predicate information (identified by operation id):

-----------------------------------------------------------

   1 - filter("RN"<=TO_NUMBER(:NEXT_NO)*20)

   4 - access("고객상태코드"='AC')

-----------------------------------------------------------

 

 

**** 환경 구성 (문제1)*****

 

DROP TABLE 고객;

 

CREATE TABLE 고객

  (  고객번호     VARCHAR2(6)

   , 고객명       VARCHAR2(50)

   , 고객상태코드 VARCHAR2(2)

   , 등록일자     DATE

   , 연락처       VARCHAR2(11)

   );

 

CREATE INDEX PK_고객 ON 고객(고객번호);

ALTER TABLE 고객 ADD CONSTRAINT PK_고객 PRIMARY KEY (고객번호); 

 

 

INSERT /*+ APPEND */ INTO 고객

SELECT LPAD(TO_CHAR(ROWNUM), 6, '0')  CUST_NO

     , LPAD(TO_CHAR(ROWNUM), 50, '0') CUST_NM

     , CASE WHEN ROWNUM <= 10000 THEN 'AC' ELSE '99' END STAT_CD

     , CASE WHEN ROWNUM <= 5000  THEN TRUNC(SYSDATE -1) ELSE TRUNC(ADD_MONTHS(SYSDATE, -3)) END INPUT_DATE

     , '01012341234' PHONE_NO

FROM DUAL

CONNECT BY LEVEL <= 100000;

 

COMMIT;

 

DROP TABLE 고객접속이력;

CREATE TABLE 고객접속이력 

   ( 접속일자        DATE

   , 고객번호        VARCHAR2(6)

   , 기타            VARCHAR2(50)

   )

  PARTITION BY RANGE (접속일자)

  (

   PARTITION P201507 VALUES LESS THAN (TO_DATE('20150801', 'YYYYMMDD'))

  , PARTITION P201508 VALUES LESS THAN (TO_DATE('20150901', 'YYYYMMDD'))

  , PARTITION P201509 VALUES LESS THAN (TO_DATE('20151001', 'YYYYMMDD'))

  , PARTITION P201510 VALUES LESS THAN (TO_DATE('20151101', 'YYYYMMDD'))

  , PARTITION P201511 VALUES LESS THAN (TO_DATE('20151201', 'YYYYMMDD'))

  , PARTITION P201512 VALUES LESS THAN (TO_DATE('20160101', 'YYYYMMDD'))

  , PARTITION P201601 VALUES LESS THAN (TO_DATE('20160201', 'YYYYMMDD'))

  , PARTITION P201602 VALUES LESS THAN (TO_DATE('20160301', 'YYYYMMDD'))

  , PARTITION P201603 VALUES LESS THAN (TO_DATE('20160401', 'YYYYMMDD'))

  , PARTITION P201604 VALUES LESS THAN (TO_DATE('20160501', 'YYYYMMDD'))

  , PARTITION P201605 VALUES LESS THAN (TO_DATE('20160601', 'YYYYMMDD'))

  , PARTITION P201606 VALUES LESS THAN (TO_DATE('20160701', 'YYYYMMDD'))

 )

;

 

CREATE INDEX PK_고객접속이력 ON 고객접속이력(접속일자, 고객번호) LOCAL;   

ALTER TABLE 고객접속이력 ADD CONSTRAINT PK_고객접속이력 PRIMARY KEY (접속일자, 고객번호); 

 

 

INSERT /*+ APPEND */ INTO 고객접속이력

SELECT TRUNC(SYSDATE-R_NUM*3) CONTACT_DATE

    ,  A.고객번호

    , '테스트'||'_'||R_NUM AS 기타

FROM 고객 A, (SELECT ROWNUM R_NUM FROM DUAL CONNECT BY LEVEL <= 100) B;

 

COMMIT;

 

 

EXEC DBMS_STATS.GATHER_TABLE_STATS('LSS','고객');

EXEC DBMS_STATS.GATHER_TABLE_STATS('LSS','고객접속이력',DEGREE=>4);

 

**** 환경 구성 (문제2)*****

** 문제 1의 테이블 활용

 

 

CREATE TABLE 고객상담이력 

   ( 고객번호        VARCHAR2(6)

   , 접속일자        DATE

   , 상담내용            VARCHAR2(50)

   )

;

 

CREATE UNIQUE INDEX PK_고객상담이력 ON 고객상담이력(고객번호, 접속일자) ;   

ALTER TABLE 고객상담이력 ADD CONSTRAINT PK_고객상담이력 PRIMARY KEY (고객번호,접속일자); 

 

 

INSERT /*+ APPEND */ INTO 고객상담이력

SELECT A.고객번호

    ,  TRUNC(SYSDATE-R_NUM*3)

    , '상담내용_메세지_테스트:'||'_'||R_NUM AS 기타

FROM 고객 A, (SELECT ROWNUM R_NUM FROM DUAL CONNECT BY LEVEL <= 100) B

;

-- 217.35

 

COMMIT;

 

EXEC DBMS_STATS.GATHER_TABLE_STATS('LSS','고객상담이력',DEGREE=>4);

 

 

-- 데이터 보정 : AC 인 데이터 중 50%는 등록일자가 한달 이후임

 

UPDATE  고객 A

SET 등록일자 =  등록일자 - 40

WHERE 고객상태코드 = 'AC'

AND ROWNUM < 5001;

 

COMMIT;

 

 

UPDATE 고객상담이력

SET 접속일자 = SYSDATE + 1000 - DBMS_RANDOM.VALUE(1,1000)  

WHERE ROWID IN (

SELECT RN

FROM (

SELECT DENSE_RANK() OVER(ORDER BY 고객번호) R, B.ROWID RN

FROM 고객상담이력 B

WHERE 고객번호 IN (SELECT 고객번호 FROM 고객 WHERE 고객상태코드 = 'AC' AND 등록일자 >= TRUNC(ADD_MONTHS(SYSDATE, -1)) )

)

WHERE R >=2500)

;

 

COMMIT;

'자격증 > SQLP - 기출복원' 카테고리의 다른 글

SQLP 11회  (0) 2020.11.11
SQLP 20회  (0) 2020.11.11
SQLP 37회  (0) 2020.11.11
SQLP 스터디 자료  (0) 2020.11.11
SQLP 기출문제  (0) 2020.11.11