当前位置: 首页 > SQL, 工具 > 正文

我们的文章会在微信公众号IT民工的龙马人生博客网站 ( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。

双节将近,最近几天都在忙于给客户做数据库节前巡检的工作。巡检过程中,性能调试是巡检最核心的内容,每次利用巡检也帮助客户优化一些TOP SQL语句。对我自己来说,每次看到有TOP SQL语句,心里面就有冲动想去做调优。

今天在调优过程中采用sql10.sql的脚本收集信息后,在分析sql性能时,有一个时间列的范围过滤,根据表的统计信息的时间和执行计划中预估的行数,可以明显的发现统计信息不准,使得SQL走错了执行计划,但是sql10.sql中并未收集相关的信息,这就是列的最小值和最大值的统计信息。这个值常常用于谓词越界的场景中,比如经典的按月建分区的情况,或者医院行业中常常根据时间来查询最近的信息等等。于是在脚本立马把这部分的信息添加上。

脚本通过引入了一个新的参数_TABLE_COL_VALUE来控制是否收集列的统计信息的最小值和最大值,默认参数的值为–,也就不收集这部分信息,原因如下:

  • 会涉及到现实列的真实值,关系到业务数据,所以安全是第一需要考虑的。
  • SQL10的执行性能,采集MIN和VALUE的最大值会调用dbms_stats存储过程中的函数,效率相对来说比较慢,可能需要几秒的时间,具体时间根据列多少和计算能力有关系。

如果想收集,在脚本开头参数定义部分将_TABLE_COL_VALUE的参数为=’ ‘即可。

注意:

目前只覆盖列类型为:date、varchar2、char、number4种类型的字段,其它字段会返回ERROR,同时返回的值也做了截断,只显示前30个字符,考虑到命令行模式的列的宽度有限。

关注公众号,后台回复“脚本”二字加群,已经加群的直接下载脚本即可。

对应代码如下:

define _TABLE_COL_VALUE    = "--"
DECLARE
     CURSOR c_stats IS
         WITH t AS
         (SELECT /*+ materialize */DISTINCT OBJECT_OWNER, OBJECT_NAME
                   FROM (SELECT OBJECT_OWNER, OBJECT_NAME
                           FROM V$SQL_PLAN
                          WHERE SQL_ID = :sql_id_bind
                            AND OBJECT_NAME IS NOT NULL
                         UNION ALL
                         SELECT OBJECT_OWNER, OBJECT_NAME
                           FROM DBA_HIST_SQL_PLAN
                          WHERE SQL_ID = :sql_id_bind
                            AND OBJECT_NAME IS NOT NULL))
         SELECT tb.OWNER,
                tb.TABLE_NAME,
                tb.COLUMN_NAME,
                tb.data_type || '(' || tb.data_length || ')' d_type,
                tb.data_type,
                s.low_value,
                s.high_value
           FROM DBA_TAB_COLS tb
           LEFT JOIN dba_tab_col_statistics s
             ON tb.owner = s.owner
            AND tb.table_name = s.table_name
            AND tb.column_name = s.column_name
          WHERE (tb.OWNER, tb.TABLE_NAME) IN
                (SELECT table_owner,table_name FROM dba_indexes
                  WHERE (owner,index_name) IN (SELECT * FROM t)
                 UNION ALL SELECT * FROM t)
          ORDER BY tb.owner, tb.table_name, tb.COLUMN_ID;

     v_number NUMBER;
     v_date DATE;
     v_varchar VARCHAR2(4000);
     v_min_readable VARCHAR2(30);
     v_max_readable VARCHAR2(30);
     v_output_line VARCHAR2(200);
BEGIN
     DBMS_OUTPUT.PUT_LINE(RPAD('OWNER', 15) || ' ' ||
                         RPAD('TABLE_NAME', 30) || ' ' ||
                         RPAD('COLUMN_NAME', 20) || ' ' ||
                         RPAD('COLUMN_TYPE', 15) || ' ' ||
                         RPAD('MIN_VALUE', 30) || ' ' ||
                         RPAD('MAX_VALUE', 30));
     DBMS_OUTPUT.PUT_LINE(RPAD('-', 15, '-') || ' ' ||
                         RPAD('-', 30, '-') || ' ' ||
                         RPAD('-', 20, '-') || ' ' ||
                         RPAD('-', 15, '-') || ' ' ||
                         RPAD('-', 30, '-') || ' ' ||
                         RPAD('-', 30, '-'));

&_TABLE_COL_VALUE     FOR rec IN c_stats LOOP
&_TABLE_COL_VALUE         BEGIN
&_TABLE_COL_VALUE             IF rec.low_value IS NULL THEN
&_TABLE_COL_VALUE                 v_min_readable := 'NULL';
&_TABLE_COL_VALUE             ELSE
&_TABLE_COL_VALUE                 CASE rec.data_type
&_TABLE_COL_VALUE                     WHEN 'NUMBER' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.low_value, v_number);
&_TABLE_COL_VALUE                         v_min_readable := TO_CHAR(v_number);
&_TABLE_COL_VALUE                     WHEN 'DATE' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.low_value, v_date);
&_TABLE_COL_VALUE                         v_min_readable := TO_CHAR(v_date, 'YYYY-MM-DD HH24:MI:SS');
&_TABLE_COL_VALUE                     WHEN 'VARCHAR2' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.low_value, v_varchar);
&_TABLE_COL_VALUE                         v_min_readable := v_varchar;
&_TABLE_COL_VALUE                     WHEN 'CHAR' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.low_value, v_varchar);
&_TABLE_COL_VALUE                         v_min_readable := v_varchar;
&_TABLE_COL_VALUE                     ELSE
&_TABLE_COL_VALUE                         v_min_readable := 'N/A';
&_TABLE_COL_VALUE                 END CASE;
&_TABLE_COL_VALUE             END IF;
&_TABLE_COL_VALUE         EXCEPTION
&_TABLE_COL_VALUE             WHEN OTHERS THEN
&_TABLE_COL_VALUE                 v_min_readable := 'ERROR';
&_TABLE_COL_VALUE         END;
&_TABLE_COL_VALUE
&_TABLE_COL_VALUE         -- 转换 MAX_VALUE
&_TABLE_COL_VALUE         BEGIN
&_TABLE_COL_VALUE             IF rec.high_value IS NULL THEN
&_TABLE_COL_VALUE                 v_max_readable := 'NULL';
&_TABLE_COL_VALUE             ELSE
&_TABLE_COL_VALUE                 CASE rec.data_type
&_TABLE_COL_VALUE                     WHEN 'NUMBER' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.high_value, v_number);
&_TABLE_COL_VALUE                         v_max_readable := TO_CHAR(v_number);
&_TABLE_COL_VALUE                     WHEN 'DATE' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.high_value, v_date);
&_TABLE_COL_VALUE                         v_max_readable := TO_CHAR(v_date, 'YYYY-MM-DD HH24:MI:SS');
&_TABLE_COL_VALUE                     WHEN 'VARCHAR2' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.high_value, v_varchar);
&_TABLE_COL_VALUE                         v_max_readable := v_varchar;
&_TABLE_COL_VALUE                     WHEN 'CHAR' THEN
&_TABLE_COL_VALUE                         DBMS_STATS.CONVERT_RAW_VALUE(rec.high_value, v_varchar);
&_TABLE_COL_VALUE                         v_max_readable := v_varchar;
&_TABLE_COL_VALUE                     ELSE
&_TABLE_COL_VALUE                         v_max_readable := 'N/A';
&_TABLE_COL_VALUE                 END CASE;
&_TABLE_COL_VALUE             END IF;
&_TABLE_COL_VALUE         EXCEPTION
&_TABLE_COL_VALUE             WHEN OTHERS THEN
&_TABLE_COL_VALUE                 v_max_readable := 'ERROR';
&_TABLE_COL_VALUE         END;
&_TABLE_COL_VALUE         IF LENGTH(v_min_readable) > 30 THEN
&_TABLE_COL_VALUE             v_min_readable := SUBSTR(v_min_readable, 1, 27) || '...';
&_TABLE_COL_VALUE         END IF;
&_TABLE_COL_VALUE
&_TABLE_COL_VALUE         IF LENGTH(v_max_readable) > 30 THEN
&_TABLE_COL_VALUE             v_max_readable := SUBSTR(v_max_readable, 1, 27) || '...';
&_TABLE_COL_VALUE         END IF;
&_TABLE_COL_VALUE         DBMS_OUTPUT.PUT_LINE(RPAD(rec.OWNER, 15) || ' ' ||
&_TABLE_COL_VALUE                             RPAD(rec.TABLE_NAME, 30) || ' ' ||
&_TABLE_COL_VALUE                             RPAD(rec.COLUMN_NAME, 20) || ' ' ||
&_TABLE_COL_VALUE                             RPAD(rec.d_type, 15) || ' ' ||
&_TABLE_COL_VALUE                             RPAD(v_min_readable, 30) || ' ' ||
&_TABLE_COL_VALUE                             RPAD(v_max_readable, 30));
&_TABLE_COL_VALUE     END LOOP;
 END;
/

——————作者介绍———————–
姓名:黄廷忠
现就职:Oracle中国高级服务团队
曾就职:OceanBase、云和恩墨、东方龙马等
电话、微信、QQ:18081072613
个人博客: (http://www.htz.pw)
CSDN地址: (https://blog.csdn.net/wwwhtzpw)
博客园地址: (https://www.cnblogs.com/www-htz-pw)


DBA必备脚本:比SQLHC好用100倍的SQL性能优化脚本版本迭代v1.0版本:等您坐沙发呢!

发表评论

gravatar

? razz sad evil ! smile oops grin eek shock ??? cool lol mad twisted roll wink idea arrow neutral cry mrgreen

快捷键:Ctrl+Enter