출처 : https://stackoverflow.com/questions/2157282/generate-days-from-date-range


-- STR_TO_DATE 파라미터에 들어가는 날짜 역순으로 해서 최대 1000개 row를 생성하여 조회조건으로 걸러낸다.

SELECT DT.YYYYMMDD
  FROM (SELECT STR_TO_DATE('20170914', '%Y%m%d') - INTERVAL(A.A + (10 * B.A) + (100 * C.A)) DAY AS YYYYMMDD
            -- SELECT DATE_ADD(STR_TO_DATE('20170914', '%Y%m%d'), INTERVAL -(A.A + (10 * B.A) + (100 * C.A)) DAY) YYYYMMDD
             , A.A AS AA
             , B.A AS BA
             , C.A AS CA
          FROM (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS A
         CROSS JOIN (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS B
         CROSS JOIN (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS C
        ) DT
 WHERE DT.YYYYMMDD BETWEEN '20170801' AND '20170914';




-- 현재 날짜 역순으로 해서 최대 1000개 row를 생성하여 조회조건으로 걸러낸다.

SELECT DT.YYYYMMDD
  FROM (SELECT DATE_ADD(NOW(), INTERVAL -(A.A + (10 * B.A) + (100 * C.A)) DAY) YYYYMMDD
             , A.A AS AA
             , B.A AS BA
             , C.A AS CA
          FROM (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS A
         CROSS JOIN (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS B
         CROSS JOIN (SELECT 0 AS A UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS C
        ) DT
 WHERE DT.YYYYMMDD BETWEEN '20170801' AND '20170914';








Oracle 참고 : http://docs.oracle.com/cd/B28359_01/java.111/b31224/urls.htm#BEIDHCBA
                  https://docs.oracle.com/cd/B12037_01/network.101/b10776/tnsnames.htm


오라클 SID, Service Name 차이 | Oracle개념용어정리

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


지금껏 오라클을 사용하면서도 SID와 Service Name은 거의 구분해서 사용하지 않았다.

덕분에 최근까지는 SID와 Service의 차이를 인식하지 못하고 사용해 왔다.

사실 일반적인 테스트 환경이나 소규모의 경우 한개의 DB서버에 한개의 인스턴스만 사용한다.

이런 환경에서는 SID와 Service Name을 구분할 필요가 없었던것.

단순히 구분짓자면 이렇게 말할수 있다.

SID = DB 하나의 인스턴스

Service Name = 여러개의 인스턴스를 모아 하나의 서버 혹은 시스템을 구성한것

쉽게 예를 들어보자.

서버 한대에 인스턴스를 여러개 생성하여 orcl1, orcl2 로 각각 생성했다고 하자.

각각의 인스턴스는 orcl1, orcl2 라는 SID를 갖게 된다.

해당 서버에서 두개의 인스턴스를 묶어 사용할경우, orcl 이라는 Service Name을 갖을수 있다.

이외에도 서버 두대에 설치하여 각각 미러링 처리하여 동일한 서버인것 처럼 활용할경우

각각의 서버는 서로다른 SID를 갖게 되지만 Service Name을 동일하게 하여 같은 서버 처럼 활용할수 있다

[출처] 오라클 SID와 Service Name의 차이|작성자 도토리


DBMS 서버를 기동하기 위해서는 DB서버가 기동하는 서버의 IP 그리고

DB서버가 접속을 받아들이기 위한 프로토콜에 대한 정의가 필요합니다.

오라클의 경우 인스턴스가 서버 역할을 하는 DBMS프로세스인데,

인스턴스가 기동할때 SID를 필요로 합니다.

즉 SID는 인스턴스의 이름인 셈이지요.

SID가 필요한 이유는 한 서버(H/W)에 여러개의 인스턴스가 기동될 수 있으므로

구별하는 태그가 필요하겠지요. 따라서 SID는 DB서버에서 필요한 정보입니다.

SID정보는 환경변수와, LISTENER.ORA라는 파일에서 정의 됩니다.

DB에 접속하는 클라이언트 프로그램의 경우 접속하고자 하는 오라클 인스턴스 정보를

필요로 합니다. 클라이언트 프로그램이 접속하는데 필요한 정보는 서버IP, 오라클SID, 접속프로토콜 
같은 정보가 필요하지요. 이러한 정보를 묶어서 서비스명으로 대표하고, 
이 서비스명으로 클라이언트 프로그램이 서버에 접속하는데 사용합니다.

이 정보는 클라이언트쪽의 TNSNAMES.ORA라는 파일에 정의 되어있습니다.

출처 : 네이버 지식 검색 : 정확히는 모름(?)


instance, instantiate ; 인스턴스, 인스턴스화

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

인스턴스는 추상화 개념 또는 클래스 객체, 컴퓨터 프로세스 등과 같은 템플릿이 실제 구현된 것이다. 
인스턴스화는 클래스 내의 객체에 대해 특정한 변형을 정의하고, 이름을 붙인 다음, 그것을 물리적인 
어떤 장소에 위치시키는 등의 작업을 통해, 인스턴스를 만드는 것을 의미한다.

1. 몇몇 필자들은, 객체지향 프로그래밍에서 클래스를 인스턴스화 한다는 것이, 클래스의 구체적인 인스턴스, 
즉 객체를 만드는 것이라고 말한다. 그 객체는 컴퓨터 내에서 실행시킬 수 있는 실행 파일이다.

2. 객체지향 프로그램 언어인 자바에서는, 클래스로부터 인스턴스화된 객체를, 객체라는 말 대신에 
역시 클래스라고 부름으로써 많은 사용자들을 혼란스럽게 한다. 즉 자바에서는, 특정한 클래스를 
만들기 위해 클래스를 인스턴스화하며, 그것 역시 컴퓨터 내에서 동작하는 실행 파일이다.

3. 객체지향 프로그래밍 개념이 나오기 이전의 데이터 모델링이나 프로그래밍에서는, 인스턴스화라는 것이 
관계형 데이터베이스 테이블 내에 새로운 엔트리를 만듦으로써 추상화된 객체로부터 실재(데이터가 들어있는) 
객체를 만드는 것도, 한 가지 용례였다.



======================================================================


오라클(Oracle) SID 및 DB_NAME 확인 방법


출처 : http://pangate.com/665


jdbc 에서 thin 드라이버로 오라클에 접속할 때는 SID를 알아야 한다. 
최근에는 SID로 직접 기술하여 접근하는 것보다는 service name 이라는 것을 tnsname.ora 파일에 지정해 놓고 이것을 사용한다. 아무래도 SID가 공개되는 것이 문제가 될 수 있을 것이다.  

<tnsname.ora 의 작성 예> 

PRODDB = 
  (DESCRIPTION = 
    (ADDRESS_LIST = 
      (ADDRESS = (PROTOCOL = TCP)(HOST = 152.25.24.15)(PORT = 1521))
    )
    (CONNECT_DATA = 
      (SERVICE_NAME = ORCL)
    )
  )

PRODDB = 
  (DESCRIPTION = 
    (ADDRESS_LIST = 
      (ADDRESS = (PROTOCOL = TCP)(HOST = 152.25.24.15)(PORT = 1521))
    )
    (CONNECT_DATA = 
      (SID = ORCL)
    )
  )



서비스명과 인스턴스명과 데이타베이스명과 SID는 서로 비슷한 듯 하면서 약간 다르다. 

1. 오라클 데이타베이스명을 확인하는 방법

SELECT NAME, DB_UNIQUE_NAME FROM v$database;

2. 오라클 SID를 확인하는 방법

SELECT instance FROM v$thread;



일반적인 경우 데이타베이스가 하나만으로 구성 되어 있다면 데이타베이스명이 SID가 된다. 하지만 RAC 로 구성하여 데이타베이스 두개가 동시 가동되는 경우라면 이 SID 가 서로 다를 수 있기 때문에 중복 확인해야 한다.

JDBC 로 접속할 때 url 정보 작성 방법 : 
    url=jdbc:oracle:thin:@ip주소:포트:SID
   (url=jdbc:oracle:thin:@192.168.20.1:1521:ORCL)

아래의 예제에서 보면 RAC로 묶여 있는 경우 DATABASE NAME 과 실제 INSTANCE NAME 은 서로 다를 수 있다. 데이타베이스명은 ORCL 이지만 인스턴스명은 ORCL1 과 ORCL2 로 이름이 다름.  thin 드라이브 URL 에서는 이 인스턴스명을 사용해야 한다.  

사용자명 입력: system/manager@PRODDB

다음에 접속됨:
Oracle Database 11g Release 11.2.0.3.0 - 64bit Production
With the Real Application Clusters option

SQL> SELECT NAME, DB_UNIQUE_NAME FROM V$DATABASE;

NAME               DB_UNIQUE_NAME
------------------ ------------------------------------------------------------
ORCL               ORCL

SQL> SELECT INSTANCE FROM V$THREAD;

INSTANCE
--------------------------------------------------------------------------------
ORCL1
ORCL2

SQL>




======================================================================


SID
jdbc:oracle:thin:@서버IP:서버Port:SID
jdbc:oracle:thin:@//hostname:port:sid


Service Name
jdbc:oracle:thin:@서버IP:서버Port:ServiceName
jdbc:oracle:thin:@//hostname:port/serviceName


Thin 사용
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=서버IP)(PORT=서버Port)))(CONNECT_DATA=(SERVICE_NAME=ServiceName)))



출처 : http://gent.tistory.com/39


PL/SQL(Procedure, Package)을 사용하다 보면 동적으로 쿼리(Query)를 생성하거나 텍스트(text) 쿼리를 입력 받아서 실행해야하는 경우가 있다다음 두가지 방법을 적절히 사용하면 좋은 결과를 얻을수 있다.

 

EXECUTE IMMEDIATE : Inset, Update, Delete 구문을 실행하거나 Select 구문을 실행 시 INTO를 사용하여 단일 값을 리턴 받을 때 사용

 

OPEN-FOR : Select 구문을 실행 시 Cursor를 리턴 받을 때 사용

 

주의 바인드 변수(:) 사용 시 쿼리 내부에서 변수명은 의미가 없고 변수 순서, 개수가 USING의 변수 순서, 개수와 일치해야 한다. 바인드 변수가 없다면 USING는 생략가능.

 

1. EXECUTE IMMEDIATE (INSERT, UPDATE, DELETE  등 구문 실행)

 

CREATE OR REPLACE PROCEDURE

PC_SET_HOLIDAY ( in_hldy_dte in date

, in_hldy_nm in varchar2

, in_use_yn in varchar2)

IS

v_query varchar(1000);

d_sysdate date;

BEGIN

BEGIN

-- 단일 값을 리턴받을때

EXECUTE IMMEDIATE 'SELECT SYSDATE FROM DUAL' INTO d_sysdate;

END;

 

v_query := v_query || 'INSERT INTO HOLIDAY';

v_query := v_query || ' VALUES(:1,:2,:3,:4)';

 

BEGIN

-- INSERT, UPDATE, DELETE 구문 실행

EXECUTE IMMEDIATE v_query

USING in_hldy_dte, in_hldy_nm, in_use_yn, d_sysdate;

END;

END;

 

 

2. OPEN-FOR (CURSOR를 리턴 받을 때)

 

CREATE OR REPLACE PROCEDURE

PC_GET_HOLIDAY ( in_fromdate in varchar2

, in_todate in varchar2

, out_cursor out SYS_REFCURSOR)

IS

v_query varchar(1000);

BEGIN

v_query := v_query || 'SELECT HLDY_DTE, HLDY_NM';

v_query := v_query || ' FROM HOLIDAY';

v_query := v_query || ' WHERE HLDY_DTE BETWEEN :in_fromdate';

v_query := v_query || ' AND :in_todate';

 

BEGIN

-- CURSOR를 리턴 받을때

OPEN out_cursor FOR v_query

USING in_fromdate, in_todate;

END;

END;

 

 

 

 



-- SELECT 1건 샘플

DECLARE
    TABLE_NAME USER_TABLES.TABLE_NAME%TYPE;

BEGIN
    EXECUTE IMMEDIATE 'SELECT TABLE_NAME FROM USER_TABLES WHERE ROWNUM = 1' INTO TABLE_NAME;
    DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || TABLE_NAME);

END;
/


-- SELECT CURSOR 샘플

DECLARE
    V_QUERY VARCHAR2(4000);
    R USER_TABLES%ROWTYPE;
    C1 SYS_REFCURSOR;

BEGIN
    V_QUERY := V_QUERY || 'SELECT TABLE_NAME';
    V_QUERY := V_QUERY || '     , TABLESPACE_NAME';
    V_QUERY := V_QUERY || '  FROM USER_TABLES';
    V_QUERY := V_QUERY || ' WHERE ROWNUM < :ROW_CNT';

    OPEN C1 FOR V_QUERY USING 10;

    LOOP                                                            
        FETCH C1 INTO R.TABLE_NAME, R.TABLESPACE_NAME;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME || ', TABLESPACE NAME : ' || R.TABLESPACE_NAME);
    END LOOP;

    CLOSE C1;

END;
/




참고 : http://blog.kjslab.com/20


1. 커서의 내용을 미리 정의 해 놓고 사용하는 방법

DECLARE

  CURSOR C1 IS
    SELECT TABLE_NAME
         , TABLESPACE_NAME
         , STATUS
         , PCT_FREE
      FROM USER_TABLES;

BEGIN

  FOR R IN C1 LOOP
    DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME || ', TABLESPACE NAME : ' || R.TABLESPACE_NAME);
  END LOOP;END;


-- ROW 단위로 변수 정의

DECLARE
  R USER_TABLES%ROWTYPE;
  C1 SYS_REFCURSOR;

BEGIN
  OPEN C1 FOR
   SELECT *
      FROM USER_TABLES;

    LOOP
        FETCH C1 INTO R;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME);
    END LOOP;
   
    CLOSE C1;

END;
/


-- ROW 단위로 변수 정의 후 COLUMN 단위로 사용

DECLARE
    R USER_TABLES%ROWTYPE;
    C1 SYS_REFCURSOR;

BEGIN
  OPEN C1 FOR
   SELECT TABLE_NAME
        , TABLESPACE_NAME
      FROM USER_TABLES;

    LOOP
        FETCH C1 INTO R.TABLE_NAME, R.TABLESPACE_NAME;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME || ', TABLESPACE NAME : ' || R.TABLESPACE_NAME);
    END LOOP;
    
    CLOSE C1;

END;



-- COLUMN 단위로 변수 정의

DECLARE
  TABLE_NAME USER_TABLES.TABLE_NAME%TYPE;
  C1 SYS_REFCURSOR;
 
BEGIN
  OPEN C1 FOR
   SELECT TABLE_NAME
      FROM USER_TABLES;

     
    LOOP
        FETCH C1 INTO TABLE_NAME;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || TABLE_NAME);
    END LOOP;
    
    CLOSE C1;

END;


-- TYPE 정의

DECLARE
    TYPE V_TYPE IS RECORD (TABLE_NAME USER_TABLES.TABLE_NAME%TYPE, TABLESPACE_NAME USER_TABLES.TABLESPACE_NAME%TYPE);
    R V_TYPE;
    C1 SYS_REFCURSOR;


BEGIN
  OPEN C1 FOR
   SELECT TABLE_NAME
        , TABLESPACE_NAME
      FROM USER_TABLES;

    LOOP
        FETCH C1 INTO R;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME || ', TABLESPACE NAME : ' || R.TABLESPACE_NAME);
    END LOOP;

   
    CLOSE C1;

END;




2. 커서 변수를 미리 만들어 놓고 불러서 사용하는 방법

DECLARE
   R USER_TABLES%ROWTYPE;
   C1 SYS_REFCURSOR;

BEGIN
   OPEN C1 FOR
      SELECT *
        FROM USER_TABLES;

   LOOP                                                            
      FETCH C1 INTO R;
      EXIT WHEN C1%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || R.TABLE_NAME || ', TABLESPACE NAME : ' || R.TABLESPACE_NAME);
   END LOOP;

   CLOSE C1;
END;
/



3. 동적으로 커서를 생성해서 사용하는 방법

BEGIN
   FOR C1 IN (
    SELECT TABLE_NAME
         , TABLESPACE_NAME
      FROM USER_TABLES
   )
   LOOP
      DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || C1.TABLE_NAME || ', TABLESPACE NAME : ' || C1.TABLESPACE_NAME);
   END LOOP;
END;





오라클 쿼리 실행 시간

출처 : http://mainia.tistory.com/769



1. SET TIMING ON 사용하기 

 


간혹 Stored Procedure 실행시 쿼리의 구간 별 시간을 알고

싶을 때가 있습니다. 전체 SP 수행 시간은 Object 테이블을 뒤져서

보면 되는데 각 단계별로 out print 를 찍으면서 보고 싶을 때

SET TIMING ON 을 사용하면 됩니다.

 

Set timing on; timing start 로 시작하고 timing stop

마무리를 하면 됩니다. 그럼 그 시간이 측정되어 로그에

찍히게 됩니다.

 
Execute as Script (F5) 로 실행

SET TIMING ON;

TIMING START;
SELECT * FROM DUAL;
TIMING STOP;

TIMING START;
SELECT * FROM DUAL;
TIMING STOP;




Toad Menu > View > Query Viewer 를 사용해도 된다.
Execute Statement (F9) 로 실행해야 보임


  

2. DBMS_UTILITY.GET_TIME 사용하기  

 

다른 방법은 DBMS_UTILITY.GET_TIME 을 사용하는 방법입니다.

현재시간을 리턴하게 되므로 쿼리 실행후 그 차이 값을 계산하는

방법입니다. 이것이 더 불편할거 같아요.

변수를 선언하고 그 변수에 현재 시간을 저장한 후 마지막에

값을 연산해서 초로 계산하게 되는 것이다. GET_TIME

100분의 1초를 리턴하게 됩니다.

DELARE
  START_TIME NUMBER;
  END_TIME NUMBER;
BEGIN

  START_TIME := DBMS_UTILITY.GET_TIME;
  SELECT *
    FROM DUAL;
  END_TIME := DBMS_UTILITY.GET_TIME;
  
  SELECT (:END_TIME - :START_TIME)/100 ELAPSE_TIME FROM DUAL;
END;


 

======================================================================


ORACLE서버에서 수행시간이 긴 쿼리 찾기 쿼리


SELECT ROWNUM NO,
       PARSING_SCHEMA_NAME,
       to_char(ELAPSED_TIME/(1000000 * decode(executions,null,1,0,1,executions)),999999.9999 ) 평균실행시간,
       executions 실행횟수,
       SQL_TEXT 쿼리 ,
       SQL_FULLTEXT
  FROM V$SQL
 WHERE  LAST_ACTIVE_TIME > SYSDATE-(1/24*2)
   -- AND LAST_ACTIVE_TIME  BETWEEN  to_Date('20111226163000','YYYYMMDDHH24MISS') AND to_Date('20111226170000','YYYYMMDDHH24MISS')
   -- AND ELAPSED_TIME >= 1 * 1000000 * decode(executions,null,1,0,1,executions)
   and PARSING_SCHEMA_NAME = 'ZIPCODE'
 ORDER BY 평균실행시간 DESC, 실행횟수 DESC;


SELECT TO_CHAR (SID) sid, serial# serialNumber,
       SUBSTR (TO_CHAR (last_call_et), 1, 6) executeSeconds, userName, machine,
       b.sql_text sqlText
  FROM v$session a, v$sqltext b
 WHERE username NOT IN ('SYSTEM', 'SYS')
   AND a.TYPE != 'BACKGROUND'
   AND a.status = 'ACTIVE'
   AND a.sql_address = b.address(+)
   AND a.sql_hash_value = b.hash_value(+)
  ORDER BY a.last_call_et DESC,  a.SID, a.serial#, b.address, b.hash_value, b.piece



현재 실행되고 있는 쿼리 와 실행 시간

SELECT TO_CHAR (SID) sid, serial# serialNumber,
   SUBSTR (TO_CHAR (last_call_et), 1, 6) executeSeconds, userName, machine,
   b.sql_text sqlText
  FROM v$session a, v$sqltext b
  WHERE username NOT IN ('SYSTEM', 'SYS')
   AND a.TYPE != 'BACKGROUND'
   AND a.status = 'ACTIVE'
   AND a.sql_address = b.address(+)
   AND a.sql_hash_value = b.hash_value(+)
  ORDER BY a.last_call_et DESC,
   a.SID,
   a.serial#,
   b.address,
   b.hash_value,
   b.piece


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

갑자기 퀴즈가 하고 싶어졌습니다. 지난 번 퀴즈가 너무 쉬웠다는 비난이 쏟아져서... @_@

1. 오라클 버전은 11.1.0.6입니다.

TPACK@ukja1106> select * from v$version where rownum = 1;

BANNER
-----------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
2. 파티션이 아닌 테이블 T1과 해시 파티션인 테이블 T2가 있습니다.
TPACK@ukja1106> create table t1(c1 number);

Table created.

Elapsed: 00:00:00.03
TPACK@ukja1106> create table t2(c1 number)
  2  partition by hash(c1) partitions 4;

Table created.

Elapsed: 00:00:00.01
3. 파티션이 아닌 테이블 T1에 대해 40만건을 Insert하는데는 1초가 조금 안걸립니다.
TPACK@ukja1106> insert into t1 select level from dual connect by level <= 400000;

400000 rows created.

Elapsed: 00:00:00.56
4. 해시 파티션인 테이블 T2에 대해 40만건을 Insert하는데는 무려! 6.3초가 걸립니다.
TPACK@ukja1106> insert into t2 select level from dual connect by level <= 400000;

400000 rows created.

Elapsed: 00:00:06.32
5. 자, 여기서 문제 나갑니다. 왜 이렇게 큰 성능 차이가 발생할까요?


======================================================================

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


티팩 (TPACK)

홈페이지 : https://sites.google.com/site/otpack/
설치 : https://sites.google.com/site/otpack/guide/install_guide


이전 포스트 [퀴즈] Partitioned Table과 Insert 성능에서 제가 생각한 답변이 나왔습니다. 이렇게 적어주셨습니다.

테이블이 파티셔닝에 되어 있어,
insert 할 때 insert 대상 블럭(버퍼)가 계속 바뀌게 되고,
그로 인해 logical reads가 증가하기 때문인 것 같습니다.

음... 마치 미리 답을 알고 있었다는 듯한 간단 명료한 답변입니다. ^^

이 퀴즈에서 예를 들었던 해시 파티션의 경우 매번 Insert가 이루어질 때마다 매번 다른 블록으로 들어갈 활륙이 높습니다(하지만 항상 그런 것은 아닙니다. 해시 파티션의 경우 해시값에 따라 들어갈 파티션을 결정하게 되는데 같은 해시값이 중복해서 나올 수 있기 때문입니다). 같은 원리로 레인지 파티션이나 리스트 파티션에서도 이런 문제가 발생할 수 있습니다.

가령 100개의 로우가 한번에 하나의 블록안에 들어가게 되면 Batch Processing이 이루어집니다. (또는 Array Insert라고도 합니다) Array Insert 효과에 의해서 Logical Reads도 줄어들고 Redo 데이터도 줄어듭니다.

퀴즈에서와 같이 아래와 같이 테이블을 만듭니다.

create table t1(c1 number);
create table t2(c1 number) partition by hash(c1) partitions 4;
Case 1. 가장 성능이 좋은 경우입니다. 가능한 많은 수의 로우가 한번에 하나의 블록안에 들어가므로 Array Insert의 효과가 극대화됩니다.
-- 가장 성능이 좋음. 
insert into t1 select level from dual connect by level <= 500000;
Case 2. 해시 파티션인 경우 각 로우가 매번 다른 블록으로 들어갈 확률이 높습니다. 따라서 Array Insert의 효과가 크게 감소합니다.
-- 성능이 안좋아짐
insert into t2 select level from dual connect by level <= 500000;
Case 3. 가장 성능이 안좋은 경우는 다음과 같이 건건이 Insert하는 경우입니다. Array Insert가 아예 발생하지 않기 때문입니다.
-- 성능이 가장 안좋음
begin
	for idx in 1 .. 500000 loop
		insert into t1 values(idx);
	end loop;
	commit;
end;
/
Case 4. 해시 파티션에 데이터가 추가되는 원리를 이해한다면, 아래와 같은 방법을 사용하면 Case 1과 같이 가장 성능이 좋게 만들 수 있습니다. ORA_HASH 함수를 이용해서 가능한 같은 파티션으로 정렬된 형태로 Insert하는 트릭입니다. 파티션키에 정렬된 형태로 Insert를 하는 것이 핵심입니다. 레인지 파티션이나 리스트 파티션에서도 동일한 방법을 사용할 수 있습니다.
-- 가장 성능이 좋음
insert into t2 select level from dual connect by level <= 500000 order by ora_hash(level, 3);

하지만 중요한 것은 답을 맞추고 안맞추고 아니라, 데이터를 통해서 객관적으로 검증할 수 있는가입니다. 이전에 이런 퀴즈가 있었죠.

1. 오라클에서 특정 세션(혹은 시스템)의 현재 상태를 바로 알 수 있는 가장 좋은 방법은 무엇일까요?

2. 오라클에서 특정 세션(혹은 시스템)을 하는 일을 추적할 수 있는 가장 좋은 방법은 무엇일까요?

첫번째 질문에 대한 제 대답은 "V$SESSTAT 뷰에 대해 Snapshot을 만들고, 각 Snapshot간의 Delta값을 비교한다"입니다. 즉 세션의 성능 문제를 분석하는데 있어서 가장 중요한 Snapshot 데이터는 V$SESSTAT 뷰이며, 이 데이터를 비교라는 방법을 이용해서 분석하는 것이 가장 기본적인 방법입니다. 이것이 티팩의 핵심적인 아이디어라는 것도 이미 말씀드렸습니다.

거창하게 말씀드렸는데, 더 간결 솔직하게 말하면 V$SESSTAT 뷰가 제공하는 데이터를 분석해보면 어느 정도 의미있는 분석을 할 수 있다. 요런 이야기입니다.

티팩의 Session Snapshot Report를 이용해서 위의 내용을 분석해보겠습니다.

create table t1(c1 number);
create table t2(c1 number) partition by hash(c1) partitions 4;

-- Session Snapshot 시작
exec tpack.begin_session_snapshot;

-- Case 1
insert into t1 select level from dual connect by level <= 500000;

exec tpack.add_session_snapshot;

-- Case 2
insert into t2 select level from dual connect by level <= 500000;

exec tpack.add_session_snapshot;

-- Case 3
begin
	for idx in 1 .. 500000 loop
		insert into t1 values(idx);
	end loop;
	commit;
end;
/
	
exec tpack.add_session_snapshot;

-- Case 4
insert into t2 select level from dual connect by level <= 500000 order by ora_hash(level, 3);

exec tpack.add_session_snapshot;

-- Session Snapshot Report 보기
set pages 10000
set lines 200
col item format a30
col deltas format a50
select * from table(tpack.session_snapshot_report);
결과는 다음과 같습니다.(내용이 길어셔 편집했음을 알려드립니다) DELTAS 값을 잘 분석해보시기 바랍니다.

- TYPE: STAT, WAIT, TIME, LATCH, ...
- ITEM: TYPE에 따른 항목(가령 STAT일 경우 session logical reads 등)
- START_VAL: 최초 Snapshot의 값
- END_VAL: 마지막 Snapshot의 값
- TOTAL_DELTA: END_VAL - START_VAL
- DELTAS: 각 Snapshot간의 Delta값의 흐름

TYPE       ITEM                            START_VAL    END_VAL TOTAL_DELTA DELTAS
---------- ------------------------------ ---------- ---------- ----------- --------------------------------------------------
STAT       redo size                      1343966008 1567241868   223275860 8486656->87223020->119117508->8448676
STAT       undo change vector size         423505332  485865780    62360448 1695312->26462580->32510584->1691972
STAT       session logical reads            14843144   16914592     2071448 15863->1521337->518902->15346
STAT       db block changes                 10655679   12453768     1798089 12623->762448->1010680->12338
STAT       db block gets                    11368998   13058307     1689309 13946->1144031->517777->13555
STAT       db block gets from cache         11368998   13058307     1689309 13946->1144031->517777->13555
STAT       redo entries                      5338577    6240096      901519 7792->381384->504754->7589
STAT       calls to kcmgrs                   5831580    6730131      898551 6973->380346->504279->6953
STAT       HSC Heap Segment Block Changes    4933463    5814688      881225 2827->375425->500125->2848
...
STAT       Heap Segment Array Inserts        2682231    3063258      381027 2785->375297->113->2832
...
STAT       DB time                             26491      31554        5063 162->1280->3406->215
...
위의 결과에서 HSC Heap Segment Block ChangesHeap Segment Array Inserts 두 개의 지표(불행히도 이 두 개의 유용한 지표는 11g에서 추가된 것입니다)를 잘 해석하시면 Array Insert가 미친 영향을 완벽하게 해석할 수 있습니다. 단, 티팩 자체가 발생시키는 Array Insert가 100여회 정도된다는 것을 고려해서 해석해야 합니다.

티팩이 해주는게 고작 이것이냐고 비난하지 마시기 바랍니다. 티팩이 하고자 하는 것은 성능 트러블슈팅을 위해 필요한 기본적인 데이터를 자동으로 수집하고 적절히 리포트해주는 것일 뿐, 결국 최종 해석은 사람의 몫입니다.

중요한 것은 데이터에 기반한 과학적인 분석을 하느냐 아니면 이거 아니면 저거 다 찔러보는 방식의 분석을 하느냐일 것입니다.



- TPACK 권한 부여

CREATE PUBLIC SYNONYM TPACK FOR TPACK.TPACK;
CREATE PUBLIC SYNONYM TPACK_SERVER FOR TPACK.TPACK_SERVER;
GRANT EXECUTE ON TPACK TO PUBLIC;
GRANT EXECUTE ON TPACK_SERVER TO PUBLIC; 






오라클 Table/Index Analyze 통계 확인 및 실행방법

출처 : http://jangpd007.tistory.com/248


DBMS_STATS.GATHER_TABLE_STATS
참고 : http://blog.naver.com/PostView.nhn?blogId=tyboss&logNo=70153024309
         http://egloos.zum.com/bosoa/v/1402860
         http://m.blog.daum.net/ryaniya/277

ANALYZE 란?

ANALYZE는 인덱스, 테이블, 클러스터의 통계정보를 생성 한다.

ANALYZE가 생성한 통계정보들은 비용기준(Cost-based)의 옵티마이저가 가장 효율적인 실행계획을 수립하기 위해 최소비용을 계산할 때 사용 된다.

각 오브젝트의 구조를 확인하는 것과 체인(Chain) 생성 여부를 확인할 수 있으므로 시스템의 저장공간 관리를 도와준다.

ANALYZE는 다음과 같은 통계정보를 생성하여 데이터 사전에 저장 한다.
  • - 테이블 : 총 로우의수, 총 블럭의 수, 비어있는 블럭에 쓰여질 수 있는 빈 공간의 평군, 체인이 발생된 로우의 수, 로우의 평균 길이
  • - 인덱스 : 인덱스의 깊이(Depth), Leaf block의 개수, Distinct Key의 수, Leaf Blocks/Key의 평균, Data blocks/key의 평균, Clustering Factor, 가장 큰 key 값, 가장 작은 key 값
  • - 컬럼 : Distinct한 값의 수, 히스토그램 정보
  • - 클러스터 : Cluster Key당 길이의 평균
문법

object-clause : TABLE, INDEX, CLUSTER중에서 해당하는 오브젝트를 기술하고 처리할 오브젝트 명을 기술 한다.

operation : operation 옵션에는 다음 3가지중 한가지 기능을 선택할 수 있다.

  • COMPUTE : 각각의 값들을 정확하게 계산 한다. 가장 정확한 통계를 얻을 수 있지만 처리 속도가 가장 느리다.
                        (테이블에 저장되어 있는 모든 행을 대상으로 통계정보를 수집한다.)
  • ESTIMATE : 자료사전의 값과 데이터 견본을 가지고 검사해서 통계를 예상 한다. COMPUTE보다 덜 정확 하지만 처리속도가 훨씬 빠르다
                        (오라클 서버의 자동화 알고리즘에 의해 데이터를 추출하여 통계정보를 수집한다.)
  • DELETE : 테이블의 모든 통계 정보를 삭제 한다
                     (수집되어 있는 통계정보를 삭제한다.)

정보수집

주기적인 ANALYZE 작업을 수행 시켜 주어야 한다.

테이블을 재생성 하거나, 새로 클러스터링을 한 경우, 인덱스를 추가하거나 재생성한 경우, 다량의 데이터를 SQL이나 배치 애플리케이션을 통해 작업한 경우 ANALYZE를 수행 시켜 주는 것이 좋다.

사용자는 USER_TABLES, USER_COLUMNS, USER_INDEXS, USER_CLUSTER 등의 자료사전 뷰를 통해 정보를 확인할 수 있다

테이블을 ANALYZE 시킨다면 거기에 따르는 인덱스들도 같이 실시하는 것이 좋다.


오 라클에서는 20,000건 이하의 행수를 가진 데이터에 대해서는 COMPUTE STATISTICS절의 사용을 권장하며 20,000건 이상되는 경우에는 ESTIMATE STATISTICS절의 사용을 권장하고 있다. 또한, 통계정보의 분석은비 일과시간에 수행하는게 원칙이며 일과 시간에 수행해야 하는 경우라면 ESTIMATE STATISTICS절의 사용을 권장한다.



테이블 정보수집 예제
SQL> ANALYZE TABLE emp COMPUTE STATISTICS ;

새로운 정보를 구하기 전에 기존 정보를 삭제
SQL> ANALYZE TABLE emp DELETE STATISTICS;


특정 column에 대한 data 분포 수집
SQL> ANALYZE TABLE emp COMPUTE STATISTICS FOR ALL INDEXED COLUMNS;


통계 정보의 확인 예제

 SQL> SELECT NUM_ROWS
, BLOCKS
, EMPTY_BLOCKS
, AVG_SPACE
, CHAIN_CNT
, AVG_ROW_LEN
, SAMPLE_SIZE
, LAST_ANALYZED
FROM USER_TABLES
WHERE TABLE_NAME = 'CMS_CATEGORY';
 
SQL> SELECT NUM_DISTINCT
, DENSITY
, LOW_VALUE
, HIGH_VALUE
, LAST_ANALYZED
, COLUMN_NAME
FROM USER_TAB_COL_STATISTICS
WHERE TABLE_NAME = 'CMS_CATEGORY';
 
오라클을 통해 작업할 시
insert시점에서 정상적으로 인덱싱이 되지 않는 경우가 발생한다.
특히 결합인덱스를 많이 사용하고 있는 경우 발생될 확률이 높다.
 
이런경우 오라클의 Analyzed를 통해서 해결이 가능하고
어느정도의 실행속도를 향상 시킬 수있다.
(실제 오라클사에서도 3개월에 한번씩은 Analyze를 실행하라 권고하고 있다.)
 

[Analyzed 확인 방법]
1-1. 테이블 통계정보
SELECT TABLE_NAME
     , BLOCKS  
-- 해당 데이터가 저장되어 있는 블록 수.
     , NUM_ROWS  -- 데이터 행 수.
     , AVG_ROW_LEN  -- 하나의 행의 평균 길이.
     , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
  FROM USER_TABLES
[WHERE TABLE_NAME = '테이블명']

1-2. 테이블 통계정보
SELECT TABLE_NAME
          , COLUMN_NAME  
-- 컬럼명
          , LOW_VALUE       -- 해당 컬럼에 저장되어 있는 최소값.
          , HIGH_VALUE      -- 해당 컬럼에 저장되어 있는 최대값.
          , NUM_DISTINCT   -- 유일한 값의 수. (히스토그램 기준)
  FROM USER_TAB_COLUMNS
[WHERE TABLE_NAME = '테이블명']

2. 인덱스 통계정보
SELECT INDEX_NAME
     , BLEVEL                        
-- 인덱스의 깊미(Depth)
     , LEAF_BLOCKS              -- 리프 블록의 수.
     , DISTINCT_KEYS            -- 인덱스 컬럼의 유일한 값의 수.
     , CLUSTERING_FACTOR  -- 조건을 만족하는 데이터를 검색할 때 인덱스 키 값이 각 블록에 얼마나 잘 분산 저장되어 있는지를 나타내는 정도.
     , NUM_ROWS                 -- 전체 행수.
     , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
  FROM USER_INDEXES
 
ex) SELECT TABLE_NAME
                , NUM_ROWS
                , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
      FROM USER_TABLES;
TABLE_NAME                       NUM_ROWS TO_CHAR(
------------------------------ ---------- --------
ABS_TYPE                               38 20040101
ANNIVERS                              183 20040101
APPRFLDRHISTORY                       570 20040101
APPRFOLDER                          16885 20040101
APPRFOLDER_ERR                       3670 20040101
APPRFORM                              359 20040101
.
.
.
USR_INFO_ADMIN                          0 20040101
VAR_DEPT_INFO                           0 20040101
VIEW_TYPE                               0 20040101
WASTEBOX                                0 20040101
ZIP_CODE                            44195 20040101
252 rows selected.
 
※ 참고 : desc user_tables 에서 보통 num_rows 로도 확인 가능
             
 
[특정 Table만 Analyze 하는 방법]
 
ANALYZE TABLE DOCUMENT COMPUTE STATISTICS
ex) DOCUMENT Table 만 Analyze
 
ANALYZE INDEX XPKDOCBOX COMPUTE STATISTICS
ex) XPKDOCBOX Index 만 Analyze
 
[전체 Table Analyze 하는 간단한 방법]
 
1. vi analyze_all.sql
    SELECT 'analyze table || table_name || estimate statistics;' FROM USER_TABLES
 
2. @analyze_all.sql
 
3. set heading off
     set echo off
     set feedback off
     set pagesize 300  (line 이 300 미만일 경우)
     spool analyze_table.sql
     /
     spool off
 
4. vi analyze_table.sql
    필요없는 Line 제거 및 정리
 
5. @analyze_table.sql
 

[전체 Index Analyze 하는 간단한 방법]
 
1. vi analyze_all.sql
    SELECT 'analyze index || index_name || estimate statistics;' FROM USER_INDEXES
 
2. @analyze_all.sql
 
3. set heading off
     set echo off
     set feedback off
     set pagesize 300  (line 이 300 미만일 경우)
     spool analyze_index.sql
     /
     spool off
 
4. vi analyze_index.sql
    필요없는 Line 제거 및 정리
 
5. @analyze_index.sql

 

===========================================================================================================================
===========================================================================================================================

 

출처 : http://blog.naver.com/vxxv122?Redirect=Log&logNo=130128144052

 

정의

→ 비용기반 옵티마이저에서 통계정보를 모아주기 위한 튜닝 도구.

    테이블과 인덱스, 클러스터, 컬럼에 대한 통계정보를 수집.

권한

→ Analyze Any 시스템 권한

통계 수집

1 . 테이블

→ Analyze Table 테이블명 Compute Statistics;

* Row수, 사용된 Block수, 사용안된 Block수, 사용가능한 평균공간, 변경된 Row수, 컬럼당 distinct 값수

  컬럼당 두번째로 가장 작은 값, 컬럼당 두번째로 가장 큰 값

   (질문) 왜 두번째로 가장 작은값과 큰값을 ?

2 . 인덱스

→ Analyze Index 인덱스명 Estimate Statistics;

* 인덱스 레벨, 레벨 Block수, distinct Key수, Key당 Leaf와 Data Block수 평균, Clustering Factor, 최소 Key 값

  최대 Key값

3 . 클러스터

→ Analyze Cluster 클러스터명 Delete Statistics;

* Cluster Key당 길이의 평균

4 . 컬럼

→ Analyze Table 테이블명 Compute Statistics For Table;

→ Analyze Table 테이블명 Compute Statistics For Columns 컬럼명 Size 75;

→ Analyze Table 테이블명 Compute Statistics For All Indexed Columns Size 75;

* 디볼트 버켓 수는 75개

  Distinct 한 값의 수, 히스토그램 정보

유효성 검사

→ Analyze Table 테이블명 Validate Structure [Cascade];

* 검사하려는 테이블과 관련된 모든 테이블을 검사하려면 Cascade 옵션 사용.

옵션

1 . Compute

→ 테이블에 저장되어 있는 모든 행을 대상으로 통계정보 수집

2 . Estimate

→ 오라클 서버의 자동화 알고리즘에 의해 데이터를 추출하여 통계정보를 수집

3 . Delete

→ 수집되어 있는 통계정보를 삭제

* 20,000건 이하의 Row는 Compute 권장, 이상은 Estimate 권장.

확인

1 . User_Tables

2 . User_Tab_Columns

3 . User_Indexes, Index_Stats, Index_histogram

4 . User_Cluster

5 . DBA_Analyze_Objects


 


===========================================================================================================================
===========================================================================================================================



출처 : http://www.oracleclub.com/article/23928

 

1. 개요
- TABLE, COLUMN, 그리고 INDEX 에 대한 통계 정보를 수집 하게 하는 PROCEDURE
 
2. SYNTAX 
DBMS_STATS.GATHER_TABLE_STATS (OWNNAME          VARCHAR2, 
                               TABNAME          VARCHAR2, 
                               PARTNAME         VARCHAR2 DEFAULT NULL,
                               ESTIMATE_PERCENT NUMBER   DEFAULT TO_ESTIMATE_PERCENT_TYPE 
                                                                            (GET_PARAM('ESTIMATE_PERCENT')), 
                               BLOCK_SAMPLE     BOOLEAN  DEFAULT FALSE,
                               METHOD_OPT       VARCHAR2 DEFAULT GET_PARAM('METHOD_OPT'),
                               DEGREE           NUMBER   DEFAULT TO_DEGREE_TYPE(GET_PARAM('DEGREE')),
                               GRANULARITY      VARCHAR2 DEFAULT GET_PARAM('GRANULARITY'), 
                               CASCADE          BOOLEAN  DEFAULT TO_CASCADE_TYPE(GET_PARAM('CASCADE')),
                               STATTAB          VARCHAR2 DEFAULT NULL, 
                               STATID           VARCHAR2 DEFAULT NULL,
                               STATOWN          VARCHAR2 DEFAULT NULL,
                               NO_INVALIDATE    BOOLEAN  DEFAULT  TO_NO_INVALIDATE_TYPE (
                                                                 GET_PARAM('NO_INVALIDATE')),
                               FORCE            BOOLEAN DEFAULT FALSE);

 

3. PARAMETER 설명
- DBMS_STATS.SET_PARAM 에 의해서 디폴트 파라미터 설정 변경이 가능하다.
- 수동 통계정보 생성 시에 저정을 하지 않았을 때 적용되는 DEFAULT 값에 영향을 미치는 값
    => CASCADE
    => DEGREE
    => ESTIMATE_PERCENT
    => METHOD_OPT
    => NO_INVALIDATE
    => GRANULARITY
- 자동 통계정보(GATHER_STATS_JOB) 시에만 영향을 미친다.
    => AUTOSTATS_TARGET [ AUTO   : ORACLE이 자동으로 대상 OBJECT 결정, 
                          ALL    : 대상 시스템의 모든 OBJECTS
                          ORACLE : SYS/SYSTEM OBJECT 만 ]

 

1) DEFAULT 값 확인
    SELECT DBMS_STATS.GET_PARAM('METHOD_OPT') FROM DUAL ;

 

    DBMS_STATS.GET_PARAM('METHOD_OPT')
    ----------------------------------
    FOR ALL COLUMNS SIZE AUTO
 
2) DEFAULT 값 변경 
    EXECUTE DBMS_STATS.SET_PARAM('METHOD_OPT', 'FOR ALL COLUMNS SIZE 1') ;

 

    PL/SQL PROCEDURE SUCCESSFULLY COMPLETED.
 
3) 변경된 DEFAULT 값 확인

    SELECT DBMS_STATS.GET_PARAM('METHOD_OPT') FROM DUAL ;

 

    DBMS_STATS.GET_PARAM('METHOD_OPT')
    ---------------------------------- 
    FOR ALL COLUMNS SIZE 1
    

Parameter

Description

OWNNAME분석할 테이블 소유자
TABNAME테이블 이름
PARTNAME파티션 이름, 지정 하지 않으면 NULL 값
ESTIMATE_PERCENT- 분석할 Row의 Percentage, NULL 이면 Compute(Row 전체)
- 유효값은 1/1000000 ~ 100 
- 디폴트로, DBMS_STATS.AUTO_SAMPLE_SIZE 에 의해서 최적의 값을 결정
BLOCK_SAMPLE- random block sampling or random row sampling 결정
- random block sampling 이 좀더 효과적이다.
- 데이터의 블록 별 분포도가 안좋을 시에는 부적절한 정보 생성
- 디폴트 값이 False로, random row sampling 을 수행한다.
METHOD_OPT

- Histogram 생성시 사용하는 옵션
    * FOR ALL [ INDEXED | HIDDEN ] COLUMNS [ size_clause ]
    * FOR COLUMN [ size clause ] column | attribute [size clause] [, column|attribute [ size clause ]…]
- Size_clause := SIZE { integer | REPEAT | AUTO | SKEWONLY }
    => Integer  : Histogram Bucket 수, Max 는 1,254
    => REPEAT   : 이미 Histogram 이 있는 칼럼에 대해서만 생성
    => AUTO     : 데이터 분산도와 칼럼 부하 정도에 따라서 생성 결정
    => SKEWONLY : 데이터 분산도에 따라서 생성 결정
- 디폴트 값은 FOR ALL COLUMNS SIZE AUTO 이다.
- 즉, Histogram 의 생성여부를 Oracle 이 알아서 판단하게 된다.
- 이 경우 EX) method_opt => FOR ALL COLUMNS SIZE 1
    => 모든 컬럼에 대해서 Histogram bucket 의 수를 1로 한다.
      즉, 컬럼 내에 존재하는 여러 가지 값들의 cardinality 는 모두 동일한 값으로 간주한다. ( histogram 을 사용하지 않는다.) 
      이것은 where 조건에 들어오는 특정 컬럼에 대한 값의 변화에 따라서 PLAN 이 변경될 가능성을 없애고자 함이다.
- FOR ALL COLUMNS SIZE 1 로 설정하여 Histogram 정보가 생성되지 않도록 조치 한다.

- Histogram 설명 : 
http://wiki.gurubee.net/display/CORE/Gathering+Histogram

DEGREE- 병렬처리 정도
- 디폴트 값은 NULL 이고, CREATE TABLE, ALTER TABLE 시 설정된 DEGREE 값에 의해 정해진다.
- AUTO_DEGREE 값은 병렬처리 정도를 자동으로 결정한다.
- 이것은 1 or DEFAULT_DEGREE [ Object Size 와 CPU Count 에 의해 결정 ]
GRANULARITY- Parition table 에 대한 분석시 사용
    => ‘ALL’                  : Global, Partition, Subpartition 통계정보 수집 – Parition Table 대상
    => ‘AUTO’                 : 디폴트 값으로 ,Partition Type 에 따라서 결정 – 일반 Table 대상
    => ‘DEFAULT’              : Global, Partition 통계정보 수집, Old Version 과 호환을 위해 사용
    => ‘GLOBAL’               : Global 통계정보 수집
    => ‘GLOBAL AND PARTITION’ : SubPartition 에 대한 통계정보는 수집되지 않는다.
    => ‘PARTITION’            : Partition 통계정보 수집
    => ‘SUBPARTITION’         : SubPartition 통계정보 수집
CASCADE- 대상 테이블의 인덱스에 대한 통계수집 여부
- 인덱스 통계정보는 병렬처리가 불가능하다.
- TRUE : 대상 테이블에 관련된 index 에 대해서 통계정보 수집
STATTAB- 통계수집을 통한 기존 통계정보 Update 전에, 기존에 존재하는 통계정보를 저장할 User Stat Table 을 지정
STATID- Stattab 와 연관된 구분자 값
STATOWN- Stattab 에 지정한 User Stat Table 의 소유자가 다를 경우 지정
NO_VALIDATE- 의존적인 Cursor를 Invalidate 할지 , 안할지 결정
    => True : 관련된 Cursor 를 invalidate 하지 않는다.
    => False : 디폴트 값으로, 관련된 Cursor 를 Invalidate 한다. 
- Default 로 DBMS_STATS.AUTO_INVALIDATE 값이고, 의미는 DBMS 가 의존적 Cursor를 언제 invalidate 할지 자동으로 결정
- 이때 작용하는 Parameter는 _OPTIMIZER_INVALIDATION_PERIOD 이고, Default 롤 18000 초(5시간) 이다.
- 즉, 통계 정보 수집에 의해 통계 정보가 변경된 후 약 5시간에 걸쳐 랜덤한 시점에 해당 Cursor가 실행될 때 invalidation이 발생한다. 
- 이것을 Auto Invalidation이라고 부른다.
- 일정 시간에 걸쳐 랜덤하게 Cursor를 Invalidation함으로써 특정 시점에 Hard Parse가 한꺼번에 몰리는 현상을 피할 수 있다. 
FORCE- Lock 걸린 Table 에 대해서도 강제로 통계정보 생성

 

4. Test
1) Mission
    CASCADE => TRUE
        E 인덱스에 대한 통계정보도수집하라.
    
    CASCADE => FALSE
        E 인덱스에 대한 통계정보도수집하라.
    
    METHOD_OPT =>'FOR ALL COLUMNS SIZE 1'
        E 칼럼(HIGH AND LOW COLUMN VALUE)에 대한 통계정보도 수집하라.
    
    METHOD_OPT =>'FOR COLUMNS'
        E컬럼에 대한통계정보를 수집하지 마라

 

2) Test 
2-1) 일반 테이블
    SHOW USER

 

    USER IS"SYS"

 

    ① SCOTT의 BIG_TABLE 의 전체 테이블과 모드 인덱스를 가지고, 테이블, 칼럼(HIGHAND LOW COLUMN VALUE), 
       연관 인덱스의 통계정보를 생성한다.( COMPUTE STATISTICS )

       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT', 
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => TRUE,
                                         METHOD_OPT => 'FOR ALL COLUMNS SIZE 1');
 
    ② SCOTT의 BIG_TABLE의 15% ROW를 가지고, 테이블, 칼럼, 연관인덱스의 통계정보를 생성한다.( SAMPLE 15 PERCENT )
       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME          => 'SCOTT',
                                         TABNAME          => 'BIG_TABLE', 
                                         CASCADE          => TRUE, 
                                         ESTIMATE_PERCENT => 15);

 

    ③ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블의 통계정보를 수집하라. 
       인덱스와 칼럼에 대한 통계정보는 제외
       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => FALSE, 
                                         METHOD_OPT => 'FOR COLUMNS');
 
    ④ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블과인덱스에 대한 통계정보를 수집하라. 
       칼럼에 대한 통계정보는 제외
       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => TRUE, 
                                         METHOD_OPT => 'FOR COLUMNS');

 

    ⑤ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블과 컬럼(NO HISTOGRAM),

       그리고 인덱스에 대한 통계정보를 수집하라.
       잠시 후에 인덱스 칼럼들의 HISTOGRAM 통계정보를 수집하라.

       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SCOTT',
                                         TABNAME => 'BIG_TABLE', 
                                         CASCADE => TRUE);

       잠시 후에..

       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    =>'SCOTT',
                                          TABNAME    => 'BIG_TABLE', 
                                          CASCADE    => TRUE,
                                          METHOD_OPT => 'FOR ALL INDEXED COLUMNSSIZE 1');

 

    ⑥ SCOTT 의 BIG_TABLE 의 전체 테이블과 모드 인덱스를 가지고, 테이블과 인덱스칼럼(ONLY HIGH AND LOW )에

       대한 통계정보를 수집하라. 인덱스에 대한 통계정보는수집하지 마라.
        EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                          TABNAME    => 'BIG_TABLE', 
                                          CASCADE    => FALSE,
                                          METHOD_OPT => 'FOR ALL INDEXED COLUMNSSIZE 1');
 
2-2) PARTITIONTABLE 의 경우 
    ① 추가적으로 GRANULARITY 정보를 'ALL', 'AUTO', 'PARITION', 'GLOBAL AND PARTITION', 'GLOBAL', 'SUBPARTITION'을

       통해서 통계수집 대상 TABLE SEGMENT를 선정 가능하다.
 
 
    ② 참고 
        - LOCK VS DBMS_STATS.GATHER_TABLE_STATS
            : DML 이 LOCK 이 발생 하여도 GATHER_TABLE_STATS 는 정상적으로 진행된다.

            BEGIN
                FOR I IN 1001 .. 5000 LOOP
                    INSERT INTO CHECK_LOCK VALUES ( I , I , 'LOCK');
                END LOOP ;
            END ;
            /

        - @CHECK_USER_LOCK.SQL
            ENTER VALUE FOR USER_NAME: SCOTT10
            OLD  46: AND B.USERNAME =UPPER('&USER_NAME')
            NEW  46: AND B.USERNAME =UPPER('SCOTT10')
            
            USERNAME    SID LOCK_TYPE       MODE_HELD   MODE_REQUE LOCK_ID1 LOCK_ID2
            ---------- ---- --------------- ----------- ---------- -------- --------
            SCOTT10     151 DML             ROW-X (SX)  NONE       51782    0
            SCOTT10     151 TRANSACTION     EXCLUSIVE   NONE       131077   307

        - EXECUTE DBMS_STATS.GATHER_TABLE_STATS(OWNNAME =>'SCOTT10',TABNAME => 'CHECK_LOCK');
        
        ==> DML LOCK 과는 무관하게 진행 된다.




========================================================================


ANALYZE 와 DBMS_STATS 의 수집정보 차이

참고 : http://jedah.tistory.com/entry/ANALYZE-%EC%99%80-DBMSSTATS-%EC%9D%98-%EC%88%98%EC%A7%91%EC%A0%95%EB%B3%B4-%EC%B0%A8%EC%9D%B4


=================================================================================


index rebuild 작업

출처 : http://aozjffl.tistory.com/285


질문 :

index에 관해서 공부를 하다가 궁금한 점이 있어 이렇게 질문을 등록합니다.

테이블에 데이터가 자주 들락날락해서 delete된 데이터의 index가 그대로 남아있어, 필요이상으로 많은 공간을 차지할 때 한다는 것을 알고 있습니다.

그런데, 이렇게 필요이상으로 index가 공간을 많이 차지한다는 것을 어떻게 알 수 있을까요?





답변1 :

아래에 있는 두 방법의 결과로 판단합니다.


첫번째 방법 :

우선 분석하고자 하는 인덱스에 대한 통계정보를 생성합니다.

SQL> analyze index 인덱스명 compute statistics;
-- 수백만건 이상의 row를 지닌 테이블에 대한 인덱스인 경우 compute statistics 대신에 estimate 옵션을 사용할 것.


아래 쿼리 결과의 blevel값이 4가 넘으면 index rebuild를 하는 것이 좋습니다.
이 blevel(Branch level)이 의미하는 것은 오라클이 index access를 할 때 몇 단계를 거쳐서 블럭의 위치를 찾아가는 가와 관계가 있습니다.

SQL> select index_name, blevel
     from user_indexes
     where index_name = '인덱스명';


두번째 방법 :

아래의 쿼리를 이용하여 index_stats테이블에 추가적인 인덱스 정보를 생성합니다.

SQL> analyze index 인덱스명 validate structure;


아래 쿼리 결과의 pct_deleted가 20%이상으로 나타나면 rebuild 대상입니다.

그리고 distinctiveness는 인덱스가 만들어진 컬럼의 값이 얼마나 자주 반복되는지를 보여주는 값입니다.

예를 들면, 만일 1만건의 row와 9000건의 서로 다른 값을 가진 테이블이 있을 때 distinctiveness값은 다음과 같이 계산됩니다.

(10000-9000)*100/10000 = 10        => 컬럼의 값이 잘 분산되어 있음

또 다른 예를 들면, 1만건의 row가 있지만 2가지 값으로만 중복되어 있다면 distintiveness값은

(10000-2)*100/10000 = 99.98        => rebuild 대상이 아니라 bitmap index로 만들 대상(99%이상이면 bitmap index 대상)

SQL> select del_lf_rows*100/decode(lf_rows,0,1,lf_rows) pct_deleted,
            (lf_rows-distinct_keys)*100/decode(lf_rows,0,1,lf_rows) distinctiveness
     from index_stats
     where name = '&index_name';




답변2 :

답변2도 답변 1과 같은 내용이지만 스크립트를 사용함으로써, 한번에 여러개의 rebuild 대상 index를 찾을 수 있다는 장점이 있다.


결과 정보를 휘발성으로 저장하는데, 그 결과를 통해서 rebuild 대상을 체크할 수 있음.
주의할 점은 아래의 쿼리 실행시 Lock이 발생하므로, 업무시간 이외에 할 것을 강추함.

SQL> analyze index index_name validate structure ;


rebuild_indx.sql

REM =============================================================
REM
REM rebuild_indx.sql
REM
REM Copyright (c) Oracle Software, 1998 - 2000
REM
REM Author : Jurgen Schelfhout
REM
REM The sample program in this article is provided for educational
REM purposes only and is NOT supported by Oracle Support Services.
REM It has been tested internally, however, and works as documented.
REM We do not guarantee that it will work for you, so be sure to test
REM it in your environment before relying on it.
REM
REM This script will analyze all the indexes for a given schema
REM or for a subset of schema's. After this the dynamic view
REM index_stats is consulted to see if an index is a good
REM candidate for a rebuild or for a bitmap index.
REM
REM Database Version : 7.3.X and above.
REM
REM =============================================================

prompt
ACCEPT spoolfile CHAR prompt 'Output-file : ';
ACCEPT schema CHAR prompt 'Schema name (% allowed) : ';
prompt
prompt
prompt Rebuild the index when :
prompt - deleted entries represent 20% or more of the current entries
prompt - the index depth is more then 4 levels.
prompt Possible candidate for bitmap index :
prompt - when distinctiveness is more than 99%
prompt
spool &spoolfile

set serveroutput on
set verify off
declare
c_name INTEGER;
ignore INTEGER;
height index_stats.height%TYPE := 0;
lf_rows index_stats.lf_rows%TYPE := 0;
del_lf_rows index_stats.del_lf_rows%TYPE := 0;
distinct_keys index_stats.distinct_keys%TYPE := 0;
cursor c_indx is
select owner, table_name, index_name
from dba_indexes
where owner like upper('&schema')
and owner not in ('SYS','SYSTEM');
begin
dbms_output.enable (1000000);
dbms_output.put_line ('Owner Index Name % Deleted Entries Blevel Distinctiveness');
dbms_output.put_line ('--------------- --------------------------------------- ----------------- ------ ---------------');

c_name := DBMS_SQL.OPEN_CURSOR;
for r_indx in c_indx loop
DBMS_SQL.PARSE(c_name,'analyze index ' || r_indx.owner || '.' ||
r_indx.index_name || ' validate structure',DBMS_SQL.NATIVE);
ignore := DBMS_SQL.EXECUTE(c_name);

select HEIGHT, decode (LF_ROWS,0,1,LF_ROWS), DEL_LF_ROWS,
decode (DISTINCT_KEYS,0,1,DISTINCT_KEYS)
into height, lf_rows, del_lf_rows, distinct_keys
from index_stats;
--
-- Index is considered as candidate for rebuild when :
-- - when deleted entries represent 20% or more of the current entries
-- - when the index depth is more then 4 levels.(height starts counting from 1 so > 5)
-- Index is (possible) candidate for a bitmap index when :
-- - distinctiveness is more than 99%
--
if ( height > 5 ) OR ( (del_lf_rows/lf_rows) > 0.2 ) then
dbms_output.put_line (rpad(r_indx.owner,16,' ') || rpad(r_indx.index_name,40,' ') ||
lpad(round((del_lf_rows/lf_rows)*100,3),17,' ') ||
lpad(height-1,7,' ') || lpad(round((lf_rows-distinct_keys)*100/lf_rows,3),16,' '));
end if;

end loop;
DBMS_SQL.CLOSE_CURSOR(c_name);
end;
/

spool off
set verify on
- - - - - - - - - - - - - - - - Code ends here - - - - - - - - - - - - - - - -


샘플 결과

Sample Output
-------------

Rebuild the index when:

- deleted entries represent 20% or more of the current entries.
- the index depth is more then 4 levels.

Possible candidate for bitmap index:

- when distinctiveness is more than 99%

Owner Index Name % Deleted Entries Blevel Distinctivenes
--------------- ------------------------- ----------------- ------ -------------
SMS PLATMAP_I1 31.504 1 32.927
SMS PLATMAP_I2 27.682 1 29.399
SMS PLATMAP_I3 31.237 1 31.237
SMS PRODMAP_I4 8.765 5 99.9
SMS SB_CR_BOM_ITEMS_I1 34.496 2 97.356
SMS SB_CR_OS_VERSIONS_I1 51.942 1 68.063
SMS SB_CR_RELEASES_I1 34.584 1 18.426
SMS TAR_HEAD_I2 21.728 5 22.344

PL/SQL procedure successfully completed.


참조 : http://cafe.naver.com/prodba/16759
         http://oracleclub.com/article/46676




참고 : http://blog.naver.com/PostView.nhn?blogId=omg92&logNo=60137609376

ERwin 재설치(Event Log Watch 서비스 또 기동 안되는 현상 -> ERwin 기동 안됨)
 
1. 프로그램 추가삭제에서 ERwin을 제거 한다.

 

2. CA_LIC 폴더까지 제거한다. (lic98_uninstaller.zip 활용)
  A. C:\\Program Files\\CA\\SharedComponents\\CA_LIC 폴더에 lic98_unintaller.zip파일을 압축 해제 한다.
  B. rmlicense.bat 파일을 실행한다.
  C. CA_LIC 폴더가 삭제 된다.

 

3. Registry에서 Event Log Watch로 검색 하여 값을 삭제 한다.

 

4. Vista인 경우 제어판 -> 사용자계정 -> 사용자 계정 컨트롤 사용 안 함으로 체크 한다.

 

5. Erwin을 재설치 한다.

 

6. 만약 서비스에 Event Log Watch의 서비스 실행 속성이 수동으로 되어 있으면 자동으로 수정한다.

 

7. 시리얼 키 등록이 안된다면 Registry에서 해당 시리얼 키로 검색하여 값을 삭제 한다.



Report4FCR_Domain.erp


1. erwin에서 Tools -> Data Browser 선택

2. Data Browser에서 Reports -> Open Reports 클릭 후 첨부파일 (Report4FCR_Domain.erp) 선택
* 새로운 Report 만들 경우
    - File -> New Report
    - Name에 report 이름을 적고 Physical 선택 후 Category는 Column 선택
    - 왼쪽 트리에서 Column -> Table -> Name 선택, Column -> Name, Column -> Datatype 선택

3. 왼쪽 트리에서 Table Reports -> reportTableDef2 더블 클릭 또는 마우스 우클릭 후 excute.. 클릭

4. 하위 트리 메뉴가 생성되는데 마우스 우클릭 후 Export... 클릭하면 export 메뉴가 나옴
    - Export : csv로 선택
    - Presentation : Tabuilar - 중복되는 결과값은 제외
                           Tabular with duplicates - 중복과 상관없이 모두 출력


ODBC 드라이버 다운로드 URL : http://technet.tmaxsoft.com

Tibero5 32Bit ODBC 드라이버

libtbcli.dll

tbodbc_driver_installer_5_32.exe



Tibero5 64Bit ODBC 드라이버

tbodbc_driver_installer_5_64.exe

libtbcli.dll




1. ODBC 드라이버 다운로드
    1) tmax technet 사이트에 접속 후 다운로드 > 데이터베이스 > Tibero 메뉴에서 해당 버전 다운로드를 클릭한다.
    2) 최하단의 OS를 선택하면 zip 파일로 된 파일을 다운로드 한다.
    3) 다운로드 받은 파일을 압축 해제하고 binary 폴더 안에 tibero5-bin-S1410-windows32-84458-opt-tested.tar.gz 파일을 압축 해제한다.
    4) ODBC 32bit 용
        드라이버 Installer : binary\tibero5-bin-S1410-windows32-84458-opt-tested.tar\tibero5-bin-S1410-windows32-84458-opt-tested\tibero5\bin\tbodbc_driver_installer_5_32.exe
        드라이버 DLL : binary\tibero5-bin-S1410-windows32-84458-opt-tested.tar\tibero5-bin-S1410-windows32-84458-opt-tested\tibero5\bin\libtbcli.dll

    5) ODBC 64bit용
        드라이버 Installer : binary\tibero5-bin-S1410-windows32-84458-opt-tested.tar\tibero5-bin-S1410-windows32-84458-opt-tested\tibero5\client\win64\bin\tbodbc_driver_installer_5_64.exe
        드라이버 DLL : binary\tibero5-bin-S1410-windows32-84458-opt-tested.tar\tibero5-bin-S1410-windows32-84458-opt-tested\tibero5\client\win64\lib\libtbcli.dll


2. ODBC 드라이버 설치
    1) C:\Program Files\Tibero 경로에 tbodbc_driver_installer_5_32.exe 복사
    2) C:\Program Files\Tibero\bin 경로에 libtbcli.dll 복사
    3) 환경변수 추가
        변수이름 : TB_HOME
        변수값 : C:\Program Files\Tibero
    4) cmd 에서 명령어 실행
        명령어 : C:\Program Files\Tibero\tbodbc_driver_installer_5_32.exe -i
   

3. ODBC 드라이버 확인
    ※ Windows가 64bit 이면서 ODBC는 32bit로 설치한 경우에는 odbc 추가 시 32bit용 odbc 관리툴을 실행해야 한다.

    실행 > odbcad32 실행하면 OS 버전에 맞는 관리툴이 기본적으로 실행된다.
    - 32bit ODBC 관리툴 실행 : C:\Windows\SysWOW64\odbcad32.exe
    - 64bit ODBC 관리툴 실행 : C:\Windows\System32\odbcad32.exe

+ Recent posts