Oracle / SQL每个表中每列的数据样本 - 太多了?

时间:2016-07-19 18:10:41

标签: sql oracle toad

我试图从大约5个模式中的每个表中的每一列中获取数据样本。下面是我从1个模式获取此数据的示例(替换" sde"无论您运行的模式是什么)。这件作品运行良好:

select CASE when 
lead(ROWNUM) over(order by ROWNUM) is null then
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1;'  
else
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1 union all' end as qry_txt
from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG'
ORDER BY ROWNUM asc, qry_txt asc

当运行上面的结果集时,以下是输出的1行示例:

select 'HUD_TYPE' as TABLE_NAME,'HUD_TYPE_ID' as COLUMN_NAME, cast(HUD_TYPE_ID as VarChar2(50)) as SAMPLE_DATA from sde.HUD_TYPE where HUD_TYPE_ID is not null and ROWNUM=1 union all

我面临的问题是,当我运行整套工会时,它永远不会完成,我只能使用以下方式一次运行几百到几千行:< / p>

select CASE when 
lead(ROWNUM) over(order by ROWNUM) is null then
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1;'  
else
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1 union all' end as qry_txt
from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG'
ORDER BY ROWNUM asc, qry_txt asc

OFFSET 4800 ROWS FETCH NEXT 400 ROWS ONLY; --Using this method so I grab the last few hundred lines so my case statement remains valid for demo

此特定架构是最小的,只返回5,000行。是我试图为即时查询做一个不可能完成的任务?或者有没有办法让我更有效率或者将其分解成循环来以某种方式获取块?试图避免涉及我们的开发人员并创建表格,ETL等。我不是SQL专家,但如果我指出了正确的方向,我可以将其破解。 :)

提前致谢。

1 个答案:

答案 0 :(得分:1)

是的,在一个查询中这可能太多了。

由于解析错误,递归SQL或空白空间,查询可能会无可救药地缓慢此任务可能需要一些开发 - 要么是一个困难但准确的存储过程,要么是使用列统计信息的快速且不准确的版本。

为什么它可能会很慢

  1. 解析时间 Oracle的编译器通常很快,但有一些奇怪的情况,解析时间呈指数级增长。使用大量UNION ALLs就是其中一种情况。特别是对于旧版本,例如10g,其中超过500 UNION ALLs将永远运行。这些问题会影响类似的方法,例如我在this answer中展示的多表插入。所以可能没有简单的方法。

    此代码显示查询时间如何随UNION ALL呈指数级增长。这是最佳可能的情况,仅使用DUAL表。

    --Find time to execute simple statements.
    declare
        v_sql clob := 'select 1 a from dual';
        v_count number;
        v_time_before number;
        v_time_after number;
    begin
        for i in 1 .. 5000 loop
            v_sql := v_sql || ' union all select 1 a from dual';
            --Only execute every 100th iteration.
            if mod(i, 100) = 0 then
                v_time_before := dbms_utility.get_time;
                execute immediate 'select count(*) from ('||v_sql||')' into v_count;
                v_time_after := dbms_utility.get_time;
                dbms_output.put_line(i||':'||to_char(v_time_after-v_time_before));
            end if;
        end loop;
    end;
    /
    

    enter image description here

  2. 递归SQL 有时,用于准备SQL语句的SQL可能会出现问题。通常最糟糕的罪犯是动态抽样。动态采样生成查询以查找少量数据以生成动态表统计信息。通常它很快但有时候这些查询会有糟糕的执行计划并且速度很慢。您可以使用/*+ dynamic_sampling(0) */等提示禁用它。

    如果你看一下GV $ SQL,你可以找到一些使用类似提示的其他递归SQL。您可能希望复制其中一些提示,例如/*+ no_parallel */

  3. 空白空间检索第一行不一定是微不足道的事情。有可能一个表先前有一个TB的数据,有人删除了99.9%的数据,并且只有一行位于该段的末尾。 Oracle必须查看所有空白区域才能找到该行。有办法解决这个问题,比如重组桌子,但有可能有人忘了这样做。

    或者可能有十亿行,但只有一行具有该列的值。并且该列没有索引,因此必须读取整个表。

  4. 可能的解决方案

    1. 准确,痛苦的方式最准确的解决方案要求存储过程一次搜索较小的子查询块。可能对任何空的空间问题使用并行性。您可能需要暂时将值存储在表中,并在此过程中对一些慢速查询进行故障排除。一小块大小将避免解析问题,并且至少可以更容易地找到具有其他问题的子查询。

    2. 不准确,快速的方式默认情况下,Oracle会为数据库中的每个列收集优化程序统计信息。 (您需要检查DBA以确保它们没有禁用默认值,遗憾的是很多DBA都这样做。)使用默认算法,Oracle扫描每个表中的每一行并记录每列的高值和低值。

      可以从数据字典中立即读取那些高值和低值。问题是转换为原始值并不完全准确。

      以下代码来自Jonathan Lewis的this article,特别是来自匿名评论。

      create or replace function raw_to_num(i_raw raw)
      return number
      as
          m_n number;
      begin
          dbms_stats.convert_raw_value(i_raw,m_n);
          return m_n;
      end;
      /  
      
      create or replace function raw_to_date(i_raw raw)
      return date
      as
          m_n date;
      begin
          dbms_stats.convert_raw_value(i_raw,m_n);
          return m_n;
      end;
      /  
      
      create or replace function raw_to_varchar2(i_raw raw)
      return varchar2
      as
          m_n varchar2(32767);
      begin
          dbms_stats.convert_raw_value(i_raw,m_n);
          return m_n;
      end;
      / 
      

      非常快速地返回结果的查询:

      select
              table_name,
              column_name,
              decode(data_type,
                      'VARCHAR2',to_char(raw_to_varchar2(low_value)),
                      'DATE',to_char(raw_to_date(low_value)),
                      'NUMBER',to_char(raw_to_num(low_value))
              ) low_value,
              decode(data_type,
                      'VARCHAR2',to_char(raw_to_varchar2(high_value)),
                      'DATE',to_char(raw_to_date(high_value)),
                      'NUMBER',to_char(raw_to_num(high_value))
              ) high_value
      from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG'
      order by 1,2