본문 바로가기

DB/__Oracle

전체 Table Row Count 를 빠르게 구하는 방법

H모사에서 데이터 이행 종료후에 전체 테이블의 Row Count를 구하여 이행 완료 Check List에 기록하는 절차가 있었다.

통계 정보를 다시 갱신하는 것보다 더 빠르게 Count하는 방법이 무엇일까 고민하다가, 찾은 방법이다.

 

※ 요약

- 테이블 전체를 적절한 그룹으로 나누어 Program parallel 방식으로 Segment 크기에 따라 PK Index를 Parallel degree를 조절하여 Fast full scan으로 읽어 Count하고 결과를 테이블에 기록

 

※ 필요한 객체

1. 전체 테이블을 크기별로 그룹핑한 목록 (홀수번 그룹은 테이블 크기의 Ascending 정렬, 짝수번 그룹은 Descending 정렬) - Balancing SQL 이용

2. MIG_TAB_CNT_VRF_LOG 테이블: Count 결과 기록 용도

3. MIG_TAB_CNT_PRC 프로시져: 테이블 이름을 parameter로 받아서 실제 처리를 담당

 

※ 전제조건

- 테이블의 PK Index는 반드시 존재하며,  'XPK_'로 이름이 시작됨

 

※ Note.

- V_SRC_TAR_GB 변수는 H사의 환경에 특화된 변수이므로 제거하고 사용할 것-

- SQL과 Procedure는 상황에 맞게 변경할 것.

 

* Balancing SQL

01.-- Parameter :GRP_NUM ==> 나누고자 하는 Group의 갯수
02.-- 홀수 Group은 작은 테이블 부터, 짝수 Group은 큰 테이블부터 순번 부여
03. 
04.WITH
05.AS
06.(
07.SELECT /*+ MATERIALIZE */ OWNER, TABLE_NAME
08.FROM ALL_TABLES
09.WHERE OWNER IN ('NISADM''NISCDM''OPUSADM''PRIADM')
10.),
11.AS
12.(
13.SELECT /*+ MATERIALIZE */
14.OWNER, SEGMENT_NAME, ROUND(SUM(BYTES)/1024/1024,0) MB
15.FROM DBA_SEGMENTS
16.WHERE OWNER IN ('NISADM''NISCDM''OPUSADM''PRIADM')
17.GROUP BY OWNER, SEGMENT_NAME
18.)
19.SELECT OWNER, TABLE_NAME, MB, GRP_NUM,
20.ROW_NUMBER() OVER(PARTITION BY GRP_NUM ORDER BY MB * DECODE(MOD(GRP_NUM, 2), 1, 1, -1)) GRP_NUM_SEQ
21.FROM (
22.SELECT /*+ ORDERED USE_HASH(T S) */
23.T.OWNER, T.TABLE_NAME, S.SEGMENT_NAME PK_NAME, MB,
24.MOD( ROW_NUMBER() OVER (ORDER BY MB NULLS LAST) , :GRP_NUM ) + 1 GRP_NUM
25.FROM LEFT OUTER JOIN S
26.ON (  T.OWNER = S.OWNER
27.--AND ( T.TABLE_NAME = REPLACE(S.SEGMENT_NAME, 'XPK', '') ) )
28.--AND ('XPK' || T.TABLE_NAME = S.SEGMENT_NAME OR T.TABLE_NAME = S.SEGMENT_NAME)  )
29.AND 'XPK' || T.TABLE_NAME = S.SEGMENT_NAME )
30.)
31.ORDER BY GRP_NUM, GRP_NUM_SEQ
32.;

 

*. MIG_TAB_CNT_PRC 프로시져

01.CREATE OR REPLACE PROCEDURE MIG_TAB_CNT_PRC
02.( I_WORK_SEQ    NUMBER,
03.I_SYS_NM      VARCHAR2,
04.I_ENT_NM      VARCHAR2,
05.I_OWN_NM      VARCHAR2,
06.I_TAB_NM      VARCHAR2,
07.I_WORK_GRP_ID VARCHAR2 )
08.AUTHID DEFINER
09.IS
10.V_DB_NAME       VARCHAR2(10);
11.V_CNT_STMT      VARCHAR2(4000);
12.V_COUNT         NUMBER(20);
13.V_ERRMSG        VARCHAR2(1000);
14.V_STRT_DT       DATE;
15.V_SRC_TAR_GB    VARCHAR2(1);
16.BEGIN
17.V_DB_NAME := SYS_CONTEXT ('USERENV''DB_NAME');
18.V_COUNT := NULL;
19.V_STRT_DT := SYSDATE;
20. 
21.V_SRC_TAR_GB := 'T';
22.IF I_OWN_NM = 'NISCDM' THEN
23.V_SRC_TAR_GB := 'C';
24.END IF;
25. 
26.-- COUNT sql 생성. PK가 존재하면 INDEX_FFS 지정, Segment Size에 따라서 PARALLEL_INDEX degree 조정
27.SELECT 'SELECT /*+ ' ||
28.CASE WHEN SEGMENT_NAME LIKE 'XPK%' THEN 'INDEX_FFS(A ' || SEGMENT_NAME || ') '
29.ELSE ''
30.END ||
31.CASE WHEN MB BETWEEN AND 50 THEN ''
32.WHEN MB BETWEEN 50 AND 500 THEN 'PARALLEL_INDEX(A ' || SEGMENT_NAME || ' 2)'
33.WHEN MB > 10240 THEN 'PARALLEL_INDEX(A ' || SEGMENT_NAME || ' 8)'
34.ELSE 'PARALLEL_INDEX(A ' || SEGMENT_NAME || ' 4)'
35.END ||
36.' */ COUNT(*) CNT FROM ' || I_OWN_NM || '.' || I_TAB_NM || ' A' AS CNT_SCR
37.INTO V_CNT_STMT
38.FROM (
39.SELECT SEGMENT_NAME, ROUND(BYTES/1024/1024, 0) MB,
40.ROW_NUMBER() OVER(ORDER BY DECODE(SUBSTR(SEGMENT_NAME, 1, 3), 'XPK', 0, 1) ) SEG_FILTER
41.FROM (
42.SELECT OWNER, SEGMENT_NAME,
43.BYTES / CASE
44.WHEN V_DB_NAME IN ('HJSALPSB''HJSDOM'THEN 1.75
45.WHEN V_DB_NAME IN ('NIS2010T'THEN 4
46.WHEN V_DB_NAME IN ('HJSBAT_T''HJSDOM_T'THEN 8
47.ELSE 1
48.END AS BYTES
49.FROM DBA_SEGMENTS S
50.)
51.WHERE OWNER = UPPER(I_OWN_NM)
52.AND ( SEGMENT_NAME = UPPER(I_TAB_NM)
53.OR SEGMENT_NAME = 'XPK' || UPPER(I_TAB_NM) )
54.)
55.WHERE SEG_FILTER = 1;
56. 
57.DBMS_OUTPUT.PUT_LINE(V_CNT_STMT);
58.EXECUTE IMMEDIATE V_CNT_STMT INTO V_COUNT;
59. 
60.INSERT INTO MIG_TAB_CNT_VRF_LOG
61.( MIG_TAB_CNT_VRF_LOG_SEQ, WORK_SEQ, SYS_NM, TAR_ENT_NM, TAR_TAB_NM, SRC_TAR_GB,
62.DB_NM, OWN_NM, TAB_NM, ROW_CNT, STRT_DT, FIN_DT, RSLT_GB, WORK_GRP_ID, LOG_MSG )
63.VALUES ( MIG_TAB_CNT_VRF_LOG_SEQ.NEXTVAL, I_WORK_SEQ, I_SYS_NM, I_ENT_NM, I_TAB_NM, V_SRC_TAR_GB,
64.V_DB_NAME, I_OWN_NM, I_TAB_NM, V_COUNT, V_STRT_DT, SYSDATE, 'C', I_WORK_GRP_ID,NULL);
65. 
66.COMMIT;
67. 
68.DBMS_OUTPUT.PUT_LINE(I_TAB_NM || ' : ' || TO_CHAR(V_COUNT));
69.EXCEPTION
70.WHEN OTHERS THEN
71.V_ERRMSG := SUBSTR(SQLERRM, 1, 1000);
72.DBMS_OUTPUT.PUT_LINE(CHR(10) || V_ERRMSG);
73. 
74.INSERT INTO MIG_TAB_CNT_VRF_LOG
75.( MIG_TAB_CNT_VRF_LOG_SEQ, WORK_SEQ, SYS_NM, TAR_ENT_NM, TAR_TAB_NM, SRC_TAR_GB,
76.DB_NM, OWN_NM, TAB_NM, ROW_CNT, STRT_DT, FIN_DT, RSLT_GB, WORK_GRP_ID, LOG_MSG )
77.VALUES ( MIG_TAB_CNT_VRF_LOG_SEQ.NEXTVAL, I_WORK_SEQ, I_SYS_NM, I_ENT_NM, I_TAB_NM, V_SRC_TAR_GB,
78.V_DB_NAME, I_OWN_NM, I_TAB_NM, V_COUNT, V_STRT_DT, SYSDATE, 'E', I_WORK_GRP_ID, V_ERRMSG);
79. 
80.COMMIT;
81.END;

 출처 :