首页 文章

Oracle 12c:仅为新分区收集统计信息

提问于
浏览
1

我最近在Oracle 12c中将几个大表转换为在日期字段和分区索引上使用每日间隔范围分区 . 作为数据加载过程的一部分,我在插入和提交数据后在表的末尾执行gather stats过程 . 间隔分区使得插入新数据不属于任何现有分区的上限时自动创建新分区变得更容易 . 但是,我注意到执行聚集统计过程所需的时间很长,对于行数为数亿的表需要花费数小时 . 问题是:大多数数据没有改变所以我只对收集新的分区或数据已更改的分区的统计信息感兴趣 . 有没有办法可以做到这一点?

3 回答

  • 0

    这正是incremental statistics的目的 .

    使用增量统计信息,Oracle将仅收集已更改的分区的分区统计信息 . 为每个分区构建概要,并快速组合这些概要以创建全局统计信息,而无需重新扫描整个表 .

    要启用它,您只需设置表首选项,然后收集统计信息 . 第一次聚会将会很慢,但未来的统计数据收集会更快 .

    begin
        dbms_stats.set_table_prefs('TABLE_OWNER', 'TABLE_NAME', 'incremental', 'true');
        dbms_stats.gather_table_stats('TABLE_OWNER', 'TABLE_NAME');
    end;
    /
    
  • 2

    干得好 . 按分区值收集统计信息的脚本 . 也许它可以帮助你:

    declare
      v_table_name varchar2(64) := ''; --your table
      v_key_value number := ;  -- your range value
    
      v_data_object_id number;
      v_object_name varchar2(64);
      v_object_type varchar2(64);
      v_granularity varchar2(64);
      v_part_name   varchar2(64);
    begin
      begin
        for i in (select kc.column_name
                  from user_part_key_columns kc
                  where kc.name = upper(v_table_name)
                    and kc.object_type = 'TABLE')
         loop
           execute immediate 'select /*+ first_rows */ dbms_rowid.rowid_object(rowid)
    
                              from ' || v_table_name || '
                              where '|| i.column_name || ' = '|| v_key_value || 
                              ' and rownum = 1'
            into v_data_object_id;
         end loop;
      exception
        when no_data_found then
          v_data_object_id := null;
      end;
    
      begin
        select t.subobject_name, t.OBJECT_TYPE
          into v_object_name, v_object_type
        from user_objects t
        where t.data_object_id = v_data_object_id;
        exception
          when no_data_found then
            v_object_name := null;
      end;
    
      if v_object_name is null
        then
          dbms_output.put_line ('no data found');
        else
          if v_object_type = 'TABLE SUBPARTITION'
            then 
              v_granularity := 'SUBPARTITION';
    
              select t.partition_name
                into v_part_name
              from user_tab_subpartitions t
              where t.subpartition_name = v_object_name;
    
            else
              v_granularity := 'PARTITION';
              v_part_name := v_object_name;
          end if;
    
          dbms_stats.gather_table_stats (ownname => user
                                        ,tabname => upper(v_table_name)
                                        ,partname => v_part_name
                                        ,granularity => v_granularity
                                        ,cascade => true
                                        ,no_invalidate => false);
      end if;
    end;
    
  • 2

    另一个简单的解决方案就是这个:

    BEGIN
    FOR aPart IN (SELECT TABLE_NAME, PARTITION_NAME FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'YOUR_TABLE' AND LAST_ANALYZED IS NULL) LOOP
        DBMS_STATS.GATHER_TABLE_STATS(USER, aPart.TABLE_NAME, aPart.PARTITION_NAME);
    END LOOP;
    END;
    

相关问题