遇到异常ORA-01555

Ega*_*ian 2 oracle plsql oracle10g

我遇到了一个需要解决的问题,其中在Master Db中有一个名为Scenarios的表,其中包含我必须找到其大小的所有Tablespace的详细信息.O/P应包含表大小(实际消耗)和索引大小以及行数.

因此,我编写了一个大小调整脚本(PL/SQL)来查找该特定数据库服务器上所有表空间的大小.

但是我运行了几天之后就得到了这个特殊的例外.

ORA-01555:快照太旧:回滚段号9,名称"_SYSSMU9 $"太小

我不确定是什么导致这种情况,因为数据大小不是那么大.

我附上了剧本

    SET SERVEROUTPUT ON size '10000000'
declare
TYPE cur_typ IS REF CURSOR;
a_Temp number := 0;
x_Total number := 0;
i number := 0;
c_cursor cur_typ;
query_str varchar2(500);
num_long Long;
currentScenarioDB nvarchar2(255);
tableExists number := 0;
scenarioId varchar2(50);
scenarioName varchar2(100);
dbIdentifier nvarchar2(50);
queryToFindScenarioNameAndId varchar2(400) := 'select scenarioId,name from scenarios where dbidentifier =  ';
selectQuery varchar2(400) := 'select scenarioId,name from scenarios where dbidentifier =  ';
insertStatement varchar2(2000) := 'Insert Into ScenarioTableAndIndexSize  values (:1,:2,:3,:4,:5,:6,:7) ';
-- scenarioId,scenarioname,,dbIdentifier,tablename,dataSize,IndexSize,rowNumber
tableIndexSize number := 0;
numOfRows number := 0;
rowNum number := 0;
tableDataSize number := 0;
Cursor getScenarioDb is select dbidentifier from scenarios where dbidentifier IN (select Distinct(TABLESPACE_NAME) from dba_tables);
begin
DBMS_OUTPUT.ENABLE(10000000);
execute immediate 'truncate table ScenarioTableAndIndexSize';
open getScenarioDb;
fetch getScenarioDb into currentScenarioDB;
while getScenarioDb%found
loop
queryToFindScenarioNameAndId := selectQuery || '''' || currentScenarioDB || '''';
execute immediate queryToFindScenarioNameAndId  into scenarioId,scenarioName;
              declare
              queryToFindNoofRows varchar2(1000);
        queryConstruct varchar2(32767) := '';
        outputTableInScenarioDb nvarchar2(256);
        Cursor getTablesInScenario is select DISTINCT TABLE_NAME from dba_tables where owner =  currentScenarioDB and TABLE_NAME not like 'BIN%' and table_name != 'SCENARIOTABLEANDINDEXSIZE' order by table_name;
        begin
              tableExists := 0;
        open getTablesInScenario;
        fetch getTablesInScenario into outputTableInScenarioDb;
        while getTablesInScenario%found
        loop
              queryConstruct  := 'select nvl( sum (';
              tableIndexSize  := 0;
              tableDataSize := 0;
              numOfRows := 0;
              queryToFindNoofRows := 'select count(*) from  '||  currentScenarioDB || '.' ||outputTableInScenarioDb;
              execute immediate queryToFindNoofRows into numOfRows;
              if numOfRows > 0 then
---------------------------Beginning Of Section to find Table data Size------------------------------------------------------------------------------------------------
                  declare
                      Cursor getColumnsInTables is select * from dba_tab_columns where Table_Name = outputTableInScenarioDb and owner = currentScenarioDB;
                      dbaTabColumnRow dba_tab_columns%rowtype;
                      dataType varchar2(40);
                      fields varchar2(1000);
                      begin
                      open getColumnsInTables;
                      fetch getColumnsInTables Into dbaTabColumnRow;
                      while getColumnsInTables%found
                      loop
                      dataType := dbaTabColumnRow.DATA_TYPE;
                     if dataType = 'CLOB' then
                        fields := 'nvl(DBMS_LOB.GETLENGTH(' || dbaTabColumnRow.COLUMN_NAME ||'),0)';
                     elsif dataType = 'BLOB' then
                        fields := 'nvl(DBMS_LOB.GETLENGTH('|| dbaTabColumnRow.COLUMN_NAME ||'),0)';
                     elsif dataType = 'LONG' then
                        fields := 'nvl(VSIZE(''''),0)';
                        x_Total := 0;
                        query_str := 'SELECT  ' || dbaTabColumnRow.COLUMN_NAME || '  FROM  ' || currentScenarioDB || '.' ||outputTableInScenarioDb;
                                      OPEN c_cursor FOR query_str;
                                  LOOP
                                  FETCH c_cursor INTO num_long;
                                  EXIT WHEN c_cursor%NOTFOUND;
                             a_Temp:=length(num_long);
                             x_Total:= x_Total + a_Temp;
                                  END LOOP;
                           CLOSE c_cursor;
                     else
                        fields := 'nvl(vsize(' || dbaTabColumnRow.COLUMN_NAME || '),0)';
                     end if;
                           fetch getColumnsInTables Into dbaTabColumnRow;
                         if getColumnsInTables%found then
                       queryConstruct := queryConstruct || fields||'+';
                     else
                     queryConstruct := queryConstruct || fields;
                     end if;
                      end loop;
                      end;
                                      queryConstruct := queryConstruct || '),0) as sizeOfTable from  ' || currentScenarioDB || '.' ||outputTableInScenarioDb;            
                                      --dbms_output.put_line(queryConstruct);
                                      execute immediate queryConstruct into tableDataSize;
---------------------------End Of Section to find Table data Size-------------------------------------------------------------------------------------------------------------
                  ---------------Section To find index size
                      declare
            Index_Name nvarchar2(4000);
            sql_statement varchar2(1000) := 'select nvl(USED_SPACE,0) from index_stats';
            stat1 varchar2(1000) := 'analyze index ';
            stat2 varchar2(1000) := '  validate structure';
            stat3 varchar2(2000) := '';
            size1 number := 0;
            cursor indexOnTable is select INDEX_NAME from dba_indexes where tablespace_name = currentScenarioDB and  table_name = outputTableInScenarioDb and index_type = 'NORMAL';
                begin
                open indexOnTable;
                fetch indexOnTable into Index_Name;
                while indexOnTable%found
                loop
                  stat3 := stat1 || currentScenarioDB ||'.' ||Index_Name || stat2;
                  execute immediate stat3;
                  execute immediate  sql_statement into size1;
                  tableIndexSize := tableIndexSize + size1;
                fetch indexOnTable into Index_Name;
                end loop;
                close indexOnTable;
        end;
                  -----end of section to find index size
          else
            rowNum := rowNum + 1;
          end if;
                        tableDataSize := x_Total + tableDataSize;
          execute immediate insertStatement using   scenarioId,scenarioName,currentScenarioDB,outputTableInScenarioDb,tableDataSize,tableIndexSize,numOfRows;
                           x_Total := 0;
          fetch getTablesInScenario into outputTableInScenarioDb;
    end loop;
    end;
    fetch getScenarioDb into currentScenarioDB;
  end loop;
  close getScenarioDb;
end;
Run Code Online (Sandbox Code Playgroud)

表格的大小是这样找到的:

  1. 列表项如果字段是Lob类型然后计算它的大小我使用nvl(DBMS_LOB.GETLENGTH(),0)
  2. 如果字段是Long类型,那么我遍历所有Long值并使用内置的Length()函数找到它们的大小
  3. 如果该字段是任何其他类型我使用nvl(vsize(),0)

只是为了指定用户对所有DB都有权限

然后我总结所有这些以找到表中的总数据大小.

有人可以告诉我我做错了什么或我该怎么做才能解决错误?

谢谢.

APC*_*APC 8

"有人可以告诉我,我做错了什么"

从哪儿开始?

ORA-01555

这在长时间运行的查询中会发生.Oracle的读取一致性策略保证结果集中的最后一条记录与结果集的第一条记录保持一致.换句话说,我们的查询不会返回在我们发出查询后提交的其他会话中所做的更改.Oracle通过将UNDO表空间中的记录替换为任何已更改的记录来完成此操作.当它无法做到这一点时,它会抛出SNAPSHOT TOO OLD异常.这意味着Oracle不再具有提供读一致视图所需的旧版本记录.

Oracle不再拥有数据的一个常见原因是因为我们的长时间运行的查询是PL/SQL游标循环,它发出COMMIT语句.正如您所知,COMMIT表示事务的结束,并释放Oracle为我们的会话保留的任何锁定.这显然包括我们会议对UNDO表空间的兴趣; 然后,Oracle可以自由地覆盖包含读取一致性所需数据的UNDO块.

在您的情况下,COMMIT语句是包含任何DDL语句的隐式语句 - 包括ANALYZE.这可能无关紧要,但似乎有人在您的程序运行时更新SCENARIOS表,这可能需要花费几天时间.

使用ANALYZE

由于几个原因,这很糟糕.首先是它已被弃用了很长一段时间:你是10g,你应该使用DBMS_STATS来收集统计数据.但等等,还有更多.

收集统计数据不是应该经常做的事情.在大多数系统中,统计数据实现了稳定的平台,即使在几个月之后它们也足够准确.因此,频繁收集统计数据充其量只是浪费周期.可能会更糟糕的是:新的统计数据产生的效率低于现有计划的风险.因此,实际上统计收集应该以受控方式进行.DBMS_STATS的一个优点是我们可以将其配置为监视应用于表的更改率,并仅在达到某种过期时重新收集统计信息. 了解更多.

当然,你只是使用ANALYZE来获取索引的最新空间使用情况,这让我想到了第三点:

疯狂

您可以选择您感兴趣的所有表中的每一行,并总计所有列的实际大小,如果我的方法正确,则为每列提供单独的查询.疯了吧.

Oracle提供的视图显示给定表使用的空间量.尽管USER_EXTENTS也可用,但USER_SEGMENTS应该足够了.SEGMENT_NAME是索引或表名.汇总BYTES列将为您提供每个表的足迹的确切大小.

当然,其中一些已分配的范围将是空的,因此您可能会认为这些数字会略微过高估计.但:

  1. 不对称的范围实际上是空间使用的更准确的图像,因为它允许由表保持的空间.
  2. 任何感知到的"准确性"损失都将在几秒钟而不是几天内的查询中得到偿还.
  3. 接下来,查询将返回现在的位置,而不是在三天的过程中空间使用的变化图片,因此数字更有用.

"但编写整个PL/SQL脚本背后的全部动机是获得ACTUAL而非ALLOCATED Space"

好的,我们来解决这个问题吧.你的脚本的主要问题是它解决了RBAR的问题; 实际上比RBARBAC更糟糕.因此,您发出一个查询矩阵,每个表对应一行的每一列.SQL是一种基于集合的语言,如果我们这样对待它会更好.

此过程组装一个动态查询,该查询组合单个SELECT以获取给定表的总大小和记录数.

create or replace procedure get_table_size 
    ( p_tabn in user_tables.table_name%type
      , p_num_rows out pls_integer
      , p_tot_size out pls_integer )
is
    stmt varchar2(32767) := 'select count(*), sum(';
    n_rows pls_integer;
    n_size pls_integer;
begin
    for r in ( select column_name, data_type, column_id
               from user_tab_columns
               where table_name = p_tabn
               order by column_id)
    loop
        if r.column_id != 1
        then
            stmt := stmt ||'+';
        end if;
        stmt := stmt || 'nvl(';
        if r.data_type in ('CLOB', 'BLOB', 'BFILE') 
        then
            stmt := stmt || ' dbms_lob.getlength('||r.column_name||')';
        else
            stmt := stmt || ' vsize('||r.column_name||')';
        end if;
        stmt := stmt || 'nvl)';
    end loop;
    stmt := stmt || ') from '||p_tabn;
    execute immediate stmt into n_rows, n_size;
    p_num_rows := n_rows;
    p_tot_size := n_size;
end;
/
Run Code Online (Sandbox Code Playgroud)

它不包括块头开销(每行3个字节),但这是一个简单的算术问题.

这是在行动:

SQL> desc t34
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SEQ_NUM                                            NUMBER
 UNIQUE_ID                                          NUMBER
 NAME                                               VARCHAR2(20 CHAR)
 LONG_COL                                           CLOB

SQL>
SQL> set timing on
SQL> var n1 number
SQL> var n2 number
SQL> exec get_table_size('T34', p_num_rows=>:n1, p_tot_size=>:n2)

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.89
SQL> print n1

        N1
----------
        11

SQL> print n2

        N2
----------
    135416

SQL>
Run Code Online (Sandbox Code Playgroud)

小桌子,也许是不切实际的快速.这是一个更大的,没有clobs.

SQL> exec get_table_size('BIG_TABLE', p_num_rows=>:n1, p_tot_size=>:n2)

PL/SQL procedure successfully completed.

Elapsed: 00:00:10.65
SQL> print n1

        N1
----------
   4680640

SQL> print n2

        N2
----------
 189919606

SQL>
Run Code Online (Sandbox Code Playgroud)

经过的时间还不错,嗯?

关于索引的空间,类似的查询将起作用,仅从USER_IND_COLUMNS驱动以获取适当的列名.我认为重新分析索引比较好.它不适用于调整您在CLOB或BLOB列上可能具有的任何TEXT索引的大小.对于那些你需要使用CTX_REPORT.INDEX_SIZE()的人,虽然这会生成一个报告,你需要在oder中解析以获得有用的数字(XML格式在这方面可能会有所帮助).