dbms_stats.gather_schema_stats 프로시저를 이용해서 통계 정보를 수집하는 경우 수행 시간이 너무 많이 걸린다는 불만이 종종 있다. 이런 현상은 대부분 다음과 같은 경우에 발생한다. 

- 특정 테이블들의 크기가 매우 크다.
- 일부 큰 테이블들은 Partitioning이 되어 있다. 

즉 큰 테이블들이 있고 더구나 Partitioning까지 되어 있으니(Global/Partiton Statistics모두 수집해야 하므로) 그만큼 통계 정보 수집에 시간이 걸리기 마련이다. 하지만 Oracle 10g에서는 estimate_percent 파라미터와 method_opt 파라미터의 값이 Oracle에 의해 적절하게 판단되어 통계 정보 생성 시간을 최적화한다. 그렇다면 왜 필요 이상 많은 시간이 걸리는 경우가 발생하는가? 

이것은 이 두 파라미터에 다음과 같은 결함이 있기 때문이다.

- estimate_percent 파라미터의 값이 AUTO이다. 즉, Oracle이 테이블의 크기에 따라 적절한 샘플 크기를 선택한다는 의미이다. 대부분의 경우 Oracle은 테이블의 크기에 따라 적절한 값을 찾지만, 간혹 아주 큰 테이블에 대해 100%의 값을 사용하는 경우가 있다. 
- method_opt 파라미터의 기본값이 FOR ALL COLUMNS SIZE AUTO이다. 즉 모든 컬럼에 대해 통계정보를 수집하면 히스토그램의 생성 여부는 Oracle이 판단한다. 대부분의 경우 Oracle은 적절한 히스토그램 버킷 크기를 할당하지만, 간혹 불필요하게(가령 Unique Key에 대해) 히스토그램을 생성하는 경우가 있다.

위와 같은 상황이 발생하면 예상보다 지나치게 많은 시간이 걸리게 되는 셈이다. 

이런 상황을 해소할 수 있는 방법이 있을까? 가령 다음과 같이 하면...?

dbms_stats.gather_schema_stats(estimate_percent=>10, method_opt=>'FOR ALL INDEXED COLUMNS SIZE 1');

물론 위의 방법으로 수행 시간 문제는 해결하겠지만, 좋은 방법은 될 수 없다. 만일 크기가 작은 테이블들이 있다면 샘플 크기가 작으면 왜곡된 통계 정보가 수집될 수 있다. 만일 특정 테이블에 대해서는 모든 컬럼(ALL COLUMNS)에 대해 통계 정보를 수집해야 한다면? 또는 특정 테이블의 특정 컬럼에 대해서는 히스토그램 크기를 주고 싶다면? 

그렇다고 테이블 레벨로 일일이 dbms_stats.gather_table_stats를 호출하는 것은 너무나 지겨운 일이다. 

그래서 보통 다음과 같은 트릭을 종종 사용한다.(편의상 정확한 문법은 생략)

-- 특정 테이블 통계 정보 변경을 막음
dbms_stats.lock_table_stats(user, 'TABLE1');

-- 스키마 레벨로 통계 정보 수집
dbms_stats.gather_schema_stats(user);

-- 통계 정보 활성화 후 이 테이블만 다시 통계 정보 수집
dbms_stats.unlock_table_stats(user, 'TABLE1');
dbms_stats.gather_table_stats(user,'TABLE', estimate_percent=>10, method_opt=>'FOR ALL INDEXED COLUMNS SIZE AUTO');

위와 같이 하면 Schema 레벨로 대부분의 테이블에 대해 통계 정보를 수집한 후 특정 문제가 되는 테이블에 대해서만 별도로 통계 정보를 수집할 수 있다.

지저분한 트릭같지만 매우 유용한 방법이다.

Oracle 11g에서는 "Preference"라는 새로운 개념을 통해 위와 같은 불편을 일시에 해소하고 있다. Oracle 11g에서라면 다음과 같이 훨씬 직관적이고 깔끔한 방법을 쓸 수 있다.

-- 아래와 같이 한번만 설정
dbms_stats.set_table_prefs(user,'TABLE1', 'ESTIMATE_PERCENT', '10');
dbms_stats.set_table_prefs(user,'TABLE1','METHOD_OPT', 'FOR ALL INDEXED COLUMNS SIZE AUTO');

-- Schema 레벨에서 통계 정보 수집
dbms_stats.gather_schema_stats(user);

즉, 특정 테이블별로 Preference(선호도)를 저장할 수 있어서 10g에서와 같은 부자연스러운 트릭은 불필요한 것이다. 11g에서 가장 마음에 드는 New Feature중 하나이다.

출처: http://ukja.tistory.com/86

 
크리에이티브 커먼즈 라이선스
Creative Commons License
select
 table_name,
 to_number(
 extractvalue(
 xmltype(
 dbms_xmlgen.getxml('select count(*) c from '||table_name))
 ,'/ROWSET/ROW/C')) count
 from user_tables;
크리에이티브 커먼즈 라이선스
Creative Commons License

[오라클] 버퍼 캐쉬 비우기

데이터베이스(DB)/Tuning 2011/11/17 13:50 Posted by 크롬은24번
10g 이상
ALTER SYSTEM FLUSH BUFFER_CACHE;

ALTER SYSTEM FLUSH SHARED_POOL;

10g 이전
alter session set events = ‘immediate trace name flush_cache'; 
크리에이티브 커먼즈 라이선스
Creative Commons License
Oracle 10g에 추가된 힌트 중 아주 재밌는 것을 알게 되어 공유한다.

힌트 명은 GATHER_PLAN_STATISTICS 이다. GATHER_PLAN_STATISTICS 힌트는 "Row source execution" 정보를 활성화한다.

Row source execution 이란 Execute Plan에서 보는 Row수의 예측 값이 아닌 실제 실행 과정에서 추출된 Row 수를 보여주는 것을 의미한다. SQL Trace나 10046 Event를 이용해 Tkprof Report를 추출해본 경험이 있다면 아마 잘 알 것이다.

이 힌트의 개념을 이해하려면 일단 오라클에서 실행 정보를 보는 방법이 다음과 같이 두 개로 나누어진다는 것을 이해할 필요가 있다.
- Explain Plan
- Row Source Operation

1. Explain Plan
Explain Plan은 "Explain Plan" 명령을 통해 생성되는 실행 계획의 "예측" 정보를 의미한다. 다음 예제를 보자.

SQL> explain plan for
select * from delete_test where id < 1000;

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
Plan hash value: 1827500787

-----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                 |     1 |     8 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DELETE_TEST     |     1 |     8 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | DELETE_TEST_IDX |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("ID"<1000)

위의 정보는 통계 정보에 기반한 "예측" 정보만을 보여준다.

2. Rowsource execution
SQL Trace나 10046 Event를 이용하면 Rowsource operation, 즉 실제 수행하면서 추출한 로우수를 알 수 있다. 아래 예를 보자.

SQL> alter session set events '10046 trace name context forever, level 12';
SQL> explain plan for
select * from delete_test where id < 1000;
SQL> alter session set events '10046 trace name context off';

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  TABLE ACCESS BY INDEX ROWID DELETE_TEST (cr=85 pr=0 pw=0 time=2077 us)
   1000   INDEX RANGE SCAN DELETE_TEST_IDX (cr=43 pr=0 pw=0 time=1054 us)

Explain Plan 정보와 Row Sourc Operation을 비교해보면 예측 로우 수와 실제 로우수가 큰 차이를 보이는 것을 확인할 수 있다. 이런 정보들을 잘 확인하면 왜 Oracle이 잘못된 실행 계획을 따르는지를 추론할 수 있는 근거가 된다.

이런 기능을 한번에 할 수 있도록 해주는 것이 GATHER_PLAN_STATISTICS 힌트이다. 아래 예를 보자.

SQL> select
/*+ gather_plan_statistics */
 *
from delete_test where id < 1000;

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
-- null, null 값은 가장 최근에 실행된 쿼리를 의미한다. 만일 특정 쿼리를 지정하고 싶다면 SQL ID와 Child Number를 주면 된다.

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  04zt89r2fw2uw, child number 0
-------------------------------------
select /*+ gather_plan_statistics */  * from delete_test where id < 1000

Plan hash value: 1827500787

---------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name            | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   1 |  TABLE ACCESS BY INDEX ROWID| DELETE_TEST     |      1 |      1 |   1000 |00:00:00.01 |     140 |
|*  2 |   INDEX RANGE SCAN          | DELETE_TEST_IDX |      1 |      1 |   1000 |00:00:00.01 |      70 |


Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("ID"<1000)

위의 예를 보면 예측 로우수(E-Rows)와 실제 로우수(A-Rows) 사이에 큰 차이가 있다는 것을 알 수 있다. 이런 큰 차이로 인해 자칫 Optimizer가 잘못된 실행 계획을 선택할 확률이 높아진다.
한 가지 주의해서 볼 항목은 "Starts" 항목이다. Starts는 Row Source Operation이 수행된 회숫를 의미한다. 가령 Fetch를 수행한 회수로 이해할 수 있다. 가령 Starts=10, E-Rows=10, A-Rows=100 이었다고 하면 10*10 = 100 이므로 실제로는 예측 로우수와 실제 로우수가 일치하는 경우에 해당한다.

지 금까지 Explain Plan과 Rowsource Operation의 결과를 수동으로 비교하는 번거로운 작업을 많이 했다면, 이 새로운 GATHER_PLAN_STATISTICS 힌트를 이용해 좀 더 효율적인 SQL 튜닝 작업을 시도해보기 바란다.

엑셈 Knowledge Base
http://wiki.ex-em.com/index.php/GATHER_PLAN_STATISTICS

PS1) 복잡한 쿼리에서는 정확한 값이 나오지 않는 경우가 있다는 보고가 있다. 이 경우에는 STATISTICS_LEVEL = ALL을 줌으로써 해결 가능하다.

PS2) 한번 수집한 통계 정보가 계속 재활용되는 듯 하다. 가령 다음과 같이 했을 때...

-- 첫번째
SQL> select /*+ gather_plan_statistics */  * from delete_test t where id < 1000;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

-- 통계 정보 수집
SQL> exec dbms_stats.gather_table_stats(user, 'DELETE_TEST', cascade=>true);

-- 두번째
SQL> select /*+ gather_plan_statistics */  * from delete_test t where id < 1000;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

두 번째 경우에는 갱신된 통계 정보에 의한 예측 로우수가 나와야함에도 불구하고 계속 이전의 값이 나오는 경우가 있다. 이 경우에는 Shared Pool을 Flush하거나, Sql Id와 Child Number를 지정, 또는 SQL Text를 살짝 바꾸어서 통계 정보가 새로 수집되게 하는 방법을 사용할 수 있다.

참고 자료)
http://jonathanlewis.wordpress.com/2006/11/09/dbms_xplan-in-10g

http://jonathanlewis.wordpress.com/2007/04/26/heisenberg/

출처 : http://ukja.tistory.com/entry/오라클-팁-GATHERPLANSTATISTICS-힌트 

크리에이티브 커먼즈 라이선스
Creative Commons License
1.LONG을 LOB으로 
to_lob(컬럼명)으로 SELECT 해서 임시 테이블로 저장한다. 

to_lob(CERTIFICATION_DATA)

 2.LOB를 VARCHAR2로
dbms_lob.substr(컬럼명,가져올글자수혹은 바이트,시작위치) 로 LOB로 부터 필요한 만큼 글자를 잘라 가져와서 테이블에 저장한다. 

dbms_lob.substr(CERTIFICATION_DATA,2000,1)


2개의 임시테이블을 작성해서 LONG으로 부터 VARCHAR2로 변환했다.
 
크리에이티브 커먼즈 라이선스
Creative Commons License