본문 바로가기

DB/__Oracle

히스토그램

히스토그램

(1) 히스토그램 유형

히스토그램이 있다면 오라클인 더 정확한 카디널리티를 구할 수 있다.
특히, 분포가 균일하지 않은 컬럼으로 조회할 경우 효과를 발휘한다.

오라클이 사용하는 히스토그램으로는 아래 두가지 유형이 있다.

  • 높이균형(Height-Balanced) 히스토그램
  • 도수분포(Frequency) 히스토그램

히스토그램을 생성하려면 컬럼 통계 수집 시 버킷 개수를 2이상으로 지정하면 된다.
히스토그램 정보는 dba_histograms 또는 dba_tab_histograms 뷰를 통해 확인할 수 있다.
특히 10g에서는 dba_tab_columns 뷰에 histogram 컬림이 추가되어 히스토그램 유형을 쉽게 파악할 수 있게 되었다.

  • FREQUENCY : 값별로 빈도수를 저장하는 도수분포 히스토그램(값의 수 = 버킷 개수)
  • HEIGHT-BALANCED : 각 버킷의 높이가 동일한 높이균형 히스토그램(값의 수 > 버킷 개수)
  • NONE : 히스토그램을 생성하지 않은 경우

두 히스토그램 유형을 설명할 예제로 편항된 데이터를 가진 member 테이블을 생성하였다.

CREATE SEQUENCE seq ;

CREATE TABLE member( memb_id NUMBER , age NUMBER( 2 ) ) ;

EXEC dbms_random.seed( 0 ) ;

insert into member(memb_id, age)
select seq.nextval, dbms_random.value(1,19)  from dual connect by level <= 50;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(20,29) from dual connect by level <= 270;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(30,39) from dual connect by level <= 330;
insert into member(memb_id, age)
select seq.nextval, 40                       from dual connect by level <= 1000; --> popular value
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(41,49) from dual connect by level <= 200;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(50,59) from dual connect by level <= 100;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(60,99) from dual connect by level <= 50;
연령대인원수
20대 미만 50
20대 270
30대 330
40대 1200
50대 100
60대 이상 50

(2) 도수분포 히스토그램

'value-based 히스토그램'으로도 불리며 빈도수(frequency number)를 저장하는 히스토그램을 말한다.
도수분포 히스토그램은 컬럼마다 하나의 버킷을 할당(값의 수 = 버킷 개수)한다. 사용자가 요청한 버킷 개수가 컬럼이 가진 갑을 수보다 많거나 같을 때 사용되며, 최도 254개의 버킷암 허용하므로 값을 수가 254개가 넘는 컬럼에는 이 히스토그램을 사용할 수 없다.

SQL> SELECT COUNT( * ) ,
  2         COUNT( DISTINCT age )
  3  FROM   member ;

  COUNT(*) COUNT(DISTINCTAGE)
---------- ------------------
      2000                 89

age 값이 89개이므로 도수분포 히스토그램을 생성할 수 있다.

SQL> begin
  2      dbms_stats.gather_table_stats(user, 'member', method_opt => 'for all columns size 100') ;
  3  end;
  4  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL>
SQL> SELECT num_distinct ,
  2         num_buckets ,
  3         histogram
  4  FROM   user_tab_col_statistics
  5  WHERE  table_name = 'MEMBER'
  6  AND    column_name = 'AGE' ;

NUM_DISTINCT NUM_BUCKETS HISTOGRAM
------------ ----------- ---------------
          89          89 FREQUENCY

100개의 버킷을 요청했는데 89개의 버킷만 생성된 이유는 종류가 89개이기 때문이다.
254개를 요청하더라도 값으 수만큼만 버킷을 할당하므로 정확한 히스토그램을 위해서라면 항상 254개를 요청하는 것이 좋다.

히스토그램 정보를 조회할 수 있는 dba/all/user_histograms 뷰에는 아래 두 컬럼이 있다.

  • endpoint_value : 버킷에 할당된 컬럼 값
  • endpoint_number : endpoint_value로 정렬했을 때, 최소 값부터 현재 값까지의 누적수량

endpoint_number가 누적수량을 의미하므로 바로 앞 endpoint_value까지의 누적수량을 차감함으로써 해당 버킷의 값 빈도수를 알 수 있다.
아래 쿼리는 endpoint_value 값별로 빈도수를 구하고서 member 테이블에서 구한 연령별 실제 인원수와 비교한 쿼리인데, 값이 서로 일치하고 있다.

SQL> SELECT t.age AS "연령" ,
  2         t.cnt AS "인원수(명)" ,
  3         NVL2( h.prev , h.running_total - h.prev , h.running_total ) AS frequency ,
  4         h.running_total
  5  FROM   (
  6          SELECT age ,
  7                 COUNT( * ) AS cnt
  8          FROM   member
  9          GROUP  BY age
 10         ) t ,
 11         (
 12          SELECT endpoint_value AS age ,
 13                 endpoint_number AS running_total ,
 14                 LAG( endpoint_number ) over (
 15                                              ORDER BY endpoint_value
 16                                             ) AS prev
 17          FROM   user_histograms
 18          WHERE  table_name = 'MEMBER'
 19          AND    column_name = 'AGE'
 20         ) h
 21  WHERE  h.age = t.age ;

      연령 인원수(명)  FREQUENCY RUNNING_TOTAL
---------- ---------- ---------- -------------
         1          2          2             2
         2          3          3             5
         3          8          8            13
         4          2          2            15
         5          3          3            18
         6          2          2            20
         7          3          3            23
         8          5          5            28
         9          3          3            31
        10          1          1            32
        11          1          1            33
        12          1          1            34
        13          1          1            35
        14          2          2            37
        15          3          3            40
        16          4          4            44
        17          1          1            45
        18          4          4            49
        19          1          1            50
        20         17         17            67
        21         39         39           106
        22         32         32           138
        23         26         26           164
        24         23         23           187
        25         14         14           201
        26         24         24           225
        27         30         30           255
        28         46         46           301
        29         19         19           320
        30         28         28           348
        31         47         47           395
        32         30         30           425
        33         34         34           459
        34         21         21           480
        35         44         44           524
        36         30         30           554
        37         45         45           599
        38         39         39           638
        39         12         12           650
        40       1000       1000          1650
        41         19         19          1669
        42         27         27          1696
        43         15         15          1711
        44         36         36          1747
        45         38         38          1785
        46         22         22          1807
        47         22         22          1829
        48         17         17          1846
        49          4          4          1850
        50          5          5          1855
        51         15         15          1870
        52         11         11          1881
        53          8          8          1889
        54          8          8          1897
        55         14         14          1911
        56          9          9          1920
        57         11         11          1931
        58         12         12          1943
        59          7          7          1950
        61          2          2          1952
        62          1          1          1953
        63          1          1          1954
        64          2          2          1956
        67          3          3          1959
        68          4          4          1963
        69          1          1          1964
        70          1          1          1965
        71          1          1          1966
        73          1          1          1967
        74          1          1          1968
        75          2          2          1970
        77          1          1          1971
        78          1          1          1972
        79          1          1          1973
        80          3          3          1976
        81          1          1          1977
        82          2          2          1979
        84          1          1          1980
        85          3          3          1983
        87          2          2          1985
        88          1          1          1986
        90          1          1          1987
        91          2          2          1989
        92          1          1          1990
        94          2          2          1992
        96          2          2          1994
        97          2          2          1996
        98          3          3          1999
        99          1          1          2000

89 개의 행이 선택되었습니다.

(3) 높이균형 히스토그램

'equi-depth 히스토그램'으로 불린다. 컬럼이 가진 갑의 수보다 적은 버킷을 요청할 때 만들어 진다.
때문에 하나의 버킷이 여러 개의 값을 담당한다.

빈도 수가 많은 값(popular value)을 포함할 때는 두 개 이상의 버킷이 할당된다.

높이 균형 히스토그램을 만들기 위해 89개보다 적은 20개의 버킷을 할당한다.

SQL> begin
  2      dbms_stats.gather_table_stats(user, 'member', method_opt => 'for all columns size 20');
  3  end;
  4  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> SELECT num_distinct ,
  2         num_buckets ,
  3         histogram
  4  FROM   user_tab_col_statistics
  5  WHERE  table_name = 'MEMBER'
  6  AND    column_name = 'AGE' ;

NUM_DISTINCT NUM_BUCKETS HISTOGRAM
------------ ----------- ---------------
          89          20 HEIGHT BALANCED

user_tabl_col_statistics에는 사용자가 요청한 대로 20개의 버킷이 생성된 것으로 조회되지만 실제 히스토그램을 보면 아래와 같이 버킷이 11개만 할당되어 있다.
이는 오라클이 popluar value를 압축해서 저아하기 때문이다.

SQL> SELECT endpoint_number ,
  2         endpoint_value
  3  FROM   user_histograms
  4  WHERE  table_name = 'MEMBER'
  5  AND    column_name = 'AGE'
  6  ORDER  BY 1 ;

ENDPOINT_NUMBER ENDPOINT_VALUE
--------------- --------------
              0              1  <= 첫번째 레코드는 버킷이 아니라 '최소 값'을 표현하는 용도
              1             21
              2             25
              3             28
              4             32
              5             35
              6             38
             16             40  <= popular value
             17             43
             18             46
             19             55
             20             99

12 개의 행이 선택되었습니다.

높이균형 히스토그램일 때 endpoint_number와 endpoint_value의 의미는 아래와 같다.

  • endpoint_number : 버킷 번호
  • endpoint_value : 버킷이 담당하는 가장 큰 값

endpoint_number는 버킷 번호이므로 1씩 증가하지만 위의 쿼리 결과에서 39~40 연령대 구간의 버킷 번호는 6에서 16으로 10칸을 건너 뛰었다.
이처럼 2칸 이상을 건너 뛰는 버킷은 popular value를 포함함을 의미하며, 여기서는 10칸이나 건너 뛰었으므로 연령대가 10개 버킷만큼의 비중을 차지함을 압축해서 표현하고 있다.

압축하더라도 연령대별 비중(ratio)과 인원수를 구하는 데는 지장이 없다.(아래 쿼리를 참조)

SQL> SELECT '~' || age "연령대" ,
  2         endpoint_number ,
  3         diff ,
  4         ROUND( 100 * diff /sum( diff ) over( ) ) AS "RATIO(%)" ,
  5         ROUND( t.num_rows * diff /sum( diff ) over( ) ) AS "인원수(명)"
  6  FROM   (
  7          SELECT table_name ,
  8                 endpoint_value AS age ,
  9                 endpoint_number ,
 10                 endpoint_number - LAG( endpoint_number ) over (
 11                                                                ORDER BY endpoint_value
 12                                                               ) AS diff ,
 13                 LAG( endpoint_number ) over (
 14                                              ORDER BY endpoint_value
 15                                             ) AS prev
 16          FROM   user_histograms
 17          WHERE  table_name = 'MEMBER'
 18          AND    column_name = 'AGE'
 19         ) h ,
 20         user_tables t
 21  WHERE  h.endpoint_number > 0
 22  AND    t.table_name = h.table_name
 23  ORDER  BY 1 ;

연령대          ENDPOINT_NUMBER       DIFF   RATIO(%) 인원수(명)
--------------- --------------- ---------- ---------- ----------
~21                           1          1          5        100
~25                           2          1          5        100
~28                           3          1          5        100
~32                           4          1          5        100
~35                           5          1          5        100
~38                           6          1          5        100
~40                          16         10         50       1000
~43                          17          1          5        100
~46                          18          1          5        100
~55                          19          1          5        100
~99                          20          1          5        100

11 개의 행이 선택되었습니다.

연령별 인원수(카디널리티)를 실제로도 위와 같이 구한다면 39세와 40세가 각각 500명으로 계산될 것이다.
하지만 오라클은 popular value(40)에 대한 카디널리티를 구할 때만 버킷에 의한 계산식을 사용하고 나머지(non-popular value)는 미리 구해 놓은 density 값을 이요한다.

popular value에 대한 선택도/카디널리티 계산

조건절 값이 두 개 이상 버킷을 가진 popular value이면 아래 공식을 따른다.

선택도 = (조건절 값의 버킷 개수) / (총 버킷 개수)

popular value 40은 총 20개의 버킷 중 10개를 사용하므로 선택도는 1/2(=0.5)이다

카디널리티 = 총 로우 수 X 선택도
           = (총 로우 수) X (조건절 값의 버킷 개수) / (총 버킷 개수)
           = 2,000 X 10 / 20 = 1,000

총 로우수 2,000을 곱하면 카디널리티가 1,000으로 아래 실행계획의 Rows와 정확히 일치한다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT *
  4  FROM   member
  5  WHERE  age = 40 ;

해석되었습니다.

SQL> @?\rdbms\admin\utlxpls

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3441279308

----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |  1000 |  6000 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| MEMBER |  1000 |  6000 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("AGE"=40)

13 개의 행이 선택되었습니다.

non-popular value에 대한 선택도/카디널리티 계산

non-popular value일 때는 미리 구해 놓은 density 값을 이용한다.

카디널리티 = 총 로우 수 X 선택도 = 총 로우 수 X density

density 값은 아래와 같다.

SQL> begin
  2      dbms_stats.set_column_stats(ownname => 'SCOTT', tabname => 'MEMBER', colname => 'AGE', density => 0.2567285 );
  3  end;
  4  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> SELECT 1 /num_distinct ,
  2         density ,
  3         ROUND( density * 2000 ) AS cardinality
  4  FROM   user_tab_col_statistics
  5  WHERE  table_name = 'MEMBER'
  6  AND    column_name = 'AGE' ;

1/NUM_DISTINCT    DENSITY CARDINALITY
-------------- ---------- -----------
    .011235955   .2567285         513

density가 0.2567285이므로 총 로우수 2,000을 곱한 카디널리티는 513이다.
실제 non-popular value를 조회하는 조건절에 대한 실행계획을 보면, 아래와 같이 Row가513임을 확인할 수 있다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT  *
  4  FROM    member
  5  WHERE   age = 39 ;

해석되었습니다.

SQL> @?\rdbms\admin\utlxpls

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3441279308

----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |   513 |  3078 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| MEMBER |   513 |  3078 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("AGE"=39)

13 개의 행이 선택되었습니다.

(4) 바인드 변수 사용 시 카디널리티 계산

바인드 변수를 사용할 때 옵티마이저는 평균 분포를 가정한 실해예획을 생성한다.
때문에 컬럼 분포가 균일할 때는 문제될 것니 없지만 그렇지 않을 때는 실행 시점에 바인딩되는 값에 따라 최적이 아닌 실행계획이 수립될 수 있어 문제가 있다.

'=' 조건일 때
바인드 변수로 '='로 검색할 때 선태고는 아래와 같이 구해진다.

  • 히스토그램이 없을 때 : 1 / num_distinct 사용
  • 도수분포 히스토그램일 때 : 1 / num_distinct 사용
  • 높이균형 히스토그램일 때 : density 사용

범위검색 조건일 때
범위검색 조건이 사용되면 옵티마이저는 고정된 규칙으로 선택도를 추정하는데, 운이 좋아 그 값이 맞을 수 있지만 틀릴 가능성이 높다.
구체적으로 보면, 아래 1~4번은 선택도를 5%로 계산하고, 5~8번까지는 0.25로 계산한다.

번호조건절번호조건절
1 번호 > :no 5 번호 between :no1 and :no2
2 번호 < :no 6 번호 > :no1 and 번호 <= :no2
3 번호 >= :no 7 번호 >= :no1 and 번호 < :no2
4 번호 <= :no 8 번호 > :no1 and 번호 < :no2

따라서 테이블에 1,000개 로우가 있을 때 옵티마이저는 1~4번 조건절에 대해서는 50개 로우가 출력될 것으로 예상하고, 5~8번 조건절에 대해서는 3개 로우가 출력될 것으로 예상한다.
아래 결과로 이런 사실을 확인할 수 있다.

SQL> CREATE TABLE t1 AS
  2  SELECT ROWNUM as no
  3  FROM   dual
  4  CONNECT BY LEVEL <= 1000 ;

테이블이 생성되었습니다.

SQL> begin
  2      dbms_stats.gather_table_stats(user, 't', method_opt => 'for all columns size 254');
  3  end;
  4  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT  *
  4  FROM    t1
  5  WHERE   no <= :no ;

해석되었습니다.

SQL> SELECT *
  2  FROM   TABLE( dbms_xplan.display( NULL , NULL , 'basic rows' ) ) ;

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3617692013

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |    50 |
|   1 |  TABLE ACCESS FULL| T1   |    50 |
------------------------------------------

8 개의 행이 선택되었습니다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT  *
  4  FROM    t1
  5  WHERE   no BETWEEN :no1
  6  AND     :no2 ;

해석되었습니다.

SQL> SELECT *
  2  FROM   TABLE( dbms_xplan.display( NULL , NULL , 'basic rows' ) ) ;

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3332582666

-------------------------------------------
| Id  | Operation          | Name | Rows  |
-------------------------------------------
|   0 | SELECT STATEMENT   |      |     3 |
|   1 |  FILTER            |      |       |
|   2 |   TABLE ACCESS FULL| T1   |     3 |
-------------------------------------------

9 개의 행이 선택되었습니다.

바인드 변수를 사용했을 때 옵티마이저는 각각 50개와 3개의 카디널리티(rows)를 예상하였다.
사죵자가 실제 입력한 값과 전혀 맞지 않은 결과일 수 있지만 옵티마이저로서는 어쩔 수 없는 선택을 한 것이다.

반면 아래와 같이 상수를 사용할 때는 거의 정확한 카디널리티를 계산해 낸다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT  *
  4  FROM    t1
  5  WHERE   no <= 100 ;

해석되었습니다.

SQL> SELECT *
  2  FROM   TABLE( dbms_xplan.display( NULL , NULL , 'basic rows' ) ) ;

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3617692013

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |   100 |
|   1 |  TABLE ACCESS FULL| T1   |   100 |
------------------------------------------

8 개의 행이 선택되었습니다.

SQL> EXPLAIN PLAN
  2  FOR
  3  SELECT  *
  4  FROM    t1
  5  WHERE   no BETWEEN 500
  6  AND     600 ;

해석되었습니다.

SQL> SELECT *
  2  FROM   TABLE( dbms_xplan.display( NULL , NULL , 'basic rows' ) ) ;

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3617692013

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |   101 |
|   1 |  TABLE ACCESS FULL| T1   |   101 |
------------------------------------------

8 개의 행이 선택되었습니다.

좋은 실행계획을 위해서라면 DW, OLAP, 배치 프로그램(Loop 내에서 수행되는 쿼리 제외)에서 수행되는 쿼리는 바인드 변수보다는 상수를 사용하는 것이 좋고, 날짜 컬럼처럼 부등호, between 같은 범위 조건으로 자주 검색되는 컬럼일 때 특히 그렇다.
OLTP성 쿼리이더라도 값의 종류가 적고 분포가 균일하지 않을 때는 상수 조건을 쓰는 것이 유용할 수 있다.

(5) 결합 선택도

동적 샘플링(Dynamic Sampling)

SQL> CREATE TABLE 사원 AS
  2  SELECT empno AS 사원번호 ,
  3         ename AS 사원명 ,
  4         sal AS 급여 ,
  5         sal*0.1 AS 상여
  6  FROM   emp ;

테이블이 생성되었습니다.

SQL>
SQL> begin
  2      dbms_stats.gather_table_stats(user, '사원', method_opt => 'for columns 급여 size 254 상여 size 254');
  3  end;
  4  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> set autotrace on explain ;
SQL>
SQL> SELECT *
  2  FROM   사원
  3  WHERE  급여 >= 2000
  4  AND    상여 >= 200 ;

  사원번호 사원명           급여       상여
---------- ---------- ---------- ----------
      7566 JONES            2975      297.5
      7698 BLAKE            2850        285
      7782 CLARK            2450        245
      7788 SCOTT            3000        300
      7839 KING             5000        500
      7902 FORD             3000        300

6 개의 행이 선택되었습니다.


Execution Plan
----------------------------------------------------------
Plan hash value: 1520453074

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     2 |    34 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| 사원 |     2 |    34 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("급여">=2000 AND "상여">=200)




{code:sql}
SQL> SELECT /*+ dynamic_sampling(4) */
  2         *
  3  FROM   사원
  4  WHERE  급여 >= 2000
  5  AND    상여 >= 200 ;

  사원번호 사원명           급여       상여
---------- ---------- ---------- ----------
      7566 JONES            2975      297.5
      7698 BLAKE            2850        285
      7782 CLARK            2450        245
      7788 SCOTT            3000        300
      7839 KING             5000        500
      7902 FORD             3000        300

6 개의 행이 선택되었습니다.


Execution Plan
----------------------------------------------------------
Plan hash value: 1520453074

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     6 |   102 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| 사원 |     6 |   102 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("급여">=2000 AND "상여">=200)

Note
-----
   - dynamic sampling used for this statement

다중 컬럼 통계(Multi-column Statistics)

11g에서는 '확장형 통계(extended statistics)'라고 불리는 기능을 통해 다중 컬럼에 대한 히스토그램을 생성할 수 있다.

컬럼 그룹을 생성하는 방법은 아래와 같다.

<방법1>
var ret varchar2(30);
exec :ret := dbms_stats.create_extended_stats(user, '사원', '(급여, 상여)');
print ret;

<방법2>
begin
    dbms_stats.gather_table_stats(user, '사원', method_opt > 'for all columns size skewonly for columns (급여, 상여) size 254');
end;
/

컬럼 그룹을 삭제하는 방법은 아래와 같다.

exec dbms_stats.drop_extended_stats(user, '사원', '(급여, 상여)');

확장형 통계를 확인하는 방법은 아래와 같다

SELECT extension_name ,
       extension
FROM   dba_stat_extensions
WHERE  owner = uesr
AND    table_name = '사원' ;

아래와 같은 방법으로도 컬럼 그룹명을 확인할 수 있고 위의 extension_name과 일치한다.

SELECT dbms_stats.show_extended_stats_name( USER , '사원' , '(급여, 상여)' ) AS col_group_name
FROM   dual ;

COL_GROUP_NAME
--------------------------------------------------------------------------------------
SYS_STU5RQ9BZ2FE#O59QYNL$VC960

위의 컬럽 그룹명을 가지고 다중 컬럼의 히스토그램을 조회해 볼 수 있다.

SELECT *
FROM   dba_histograms
WHERE  owner = USER
AND    table_name = '사원'
AND    column_name = 'SYS_STU5RQ9BZ2FE#O59QYNL$VC960' ;

문서에 대하여

  • 최초작성자 : [황종필]
  • 최초작성일 : 2010년 04월 30일
  • 이 문서는 오라클클럽 코어 오라클 데이터베이스 스터디 모임에서 작성하였습니다.
  • 이 문서의 내용은 (주)비투엔컬설팅에서 출간한 '오라클 성능 고도화 원리와 해법II'를 참고하였습니다.