SQL优化——全自动SQL审核

文章目录

  • 1、抓出外键没创建索引的表
  • 2、抓出需要收集直方图的列
  • 3、抓出必须创建索引的列
  • 4、抓出SELECT * 的SQL
  • 5、抓出有标量子查询的SQL
  • 6、抓出带有自定义函数的SQL
  • 7、抓出表被多次反复调用SQL
  • 8、抓出走了FILTER的SQL
  • 9、抓出返回行数较多的嵌套循环SQL
  • 10、抓出NL被驱动表走了全表扫描的SQL
  • 11、抓出走了TABLE ACCESS FULL的SQL
  • 12、抓出走了INDEX FULL SCAN的SQL
  • 13、抓出走了INDEX SKIP SCAN的SQL
  • 14、抓出索引被哪些SQL引用
  • 15、 抓出走了笛卡儿积的SQL
  • 16、抓出走了错误的排序合并连接的SQL
  • 17、抓出LOOP套LOOP的PSQL
  • 18、抓出走了低选择性索引的SQL
  • 19、抓出可以创建组合索引的SQL(回表再过滤选择性高的列)
  • 20、抓出可以创建组合索引的SQL(回表只访问少数字段)

我们为大家分享一些常用的全自动SQL审核脚本,在实际工作中,我们可以对脚本进行适当修改,以便适应自己的数据库环境,从而提升工作效率。

1、抓出外键没创建索引的表

此脚本不依赖统计信息。

建议在外键列上创建索引,外键列不创建索引容易导致死锁。级联删除的时候,外键列没有索引会导致表被全表扫描。以下脚本抓出Scott账户下外键没创建索引的表:

with cons as (select /*+ materialize */ owner, table_name, constraint_name
          from dba_constraints
         where owner = 'SCOTT'
           AND constraint_type = 'R'),
     idx as (
     select /*+ materialize*/ table_owner,table_name, column_name
          from dba_ind_columns
         where table_owner = 'SCOTT')
select owner,table_name,constraint_name,column_name
  from dba_cons_columns
 where (owner,table_name, constraint_name) in
       (select * from cons)
   and (owner,table_name, column_name) not in
       (select * from idx);

在Scott账户中,EMP表的deptno列引用了DEPT表的deptno列,但是没有创建索引,因此我们通过脚本可以将其抓出:
在这里插入图片描述

2、抓出需要收集直方图的列

此脚本依赖统计信息。当一个表比较大,列选择性低于5%,而且列出现在where条件中,为了防止优化器估算Rows出现较大偏差,我们需要对这种列收集直方图。以下脚本抓出Scott账户下,表总行数大于5万行、列选择性低于5%并且列出现在where条件中的表以及列信息:

select a.owner,
       a.table_name,
       a.column_name,
       b.num_rows,
       a.num_distinct Cardinality,
       round(a.num_distinct / b.num_rows * 100, 2) selectivity
  from dba_tab_col_statistics a, dba_tables b
 where a.owner = b.owner
   and a.table_name = b.table_name
   and a.owner = 'SCOTT'
   and round(a.num_distinct / b.num_rows * 100, 2) < 5
   and num_rows > 50000
   and (a.table_name, a.column_name) in
       (select o.name, c.name
          from sys.col_usage$ u, sys.obj$ o, sys.col$ c, sys.user$ r
         where o.obj# = u.obj#
           and c.obj# = u.obj#
           and c.col# = u.intcol#
           and r.name = 'SCOTT');

在Scott账户中,test表总行数大于5万行,owner列选择性小于5%,而且出现在where条件中,通过以上脚本我们可以将其抓出:
在这里插入图片描述

3、抓出必须创建索引的列

此脚本依赖统计信息。当一个表比较大,列选择性超过20%,列出现在where条件中并且没有创建索引,我们可以对该列创建索引从而提升SQL查询性能。以下脚本抓出Scott账户下表总行数大于5万行、列选择性超过20%、列出现在where条件中并且没有创建索引:

select owner,
       table_name,
       column_name,
       num_rows,
       Cardinality,
       selectivity
from (select a.owner,
             a.table_name,
             a.column_name,
             b.num_rows,
             a.num_distinct                              Cardinality,
             round(a.num_distinct / b.num_rows * 100, 2) selectivity
      from dba_tab_col_statistics a,
           dba_tables b
      where a.owner = b.owner
        and a.table_name = b.table_name
        and a.owner = 'SCOTT')
where selectivity >= 20
  and num_rows > 50000
  and (table_name, column_name) not in
      (select table_name, column_name
       from dba_ind_columns
       where table_owner = 'SCOTT'
         and column_position = 1)
  and (table_name, column_name) in
      (select o.name, c.name
       from sys.col_usage$ u,
            sys.obj$ o,
            sys.col$ c,
            sys.user$ r
       where o.obj# = u.obj#
         and c.obj# = u.obj#
         and c.col# = u.intcol#
         and r.name = 'SCOTT');

在这里插入图片描述

4、抓出SELECT * 的SQL

此脚本不依赖统计信息。在开发过程中,我们应该尽量避免编写SELECT * 这种SQL。SELECT * 这种SQL,走索引无法避免回表,走HASH连接的时候会将驱动表所有的列放入PGA中,浪费PGA内存。执行计划中(V$SQL_PLAN/PLAN_TABLE),projection字段表示访问了哪些字段,如果projection字段中字段个数等于表的字段总个数,那么我们就可以判断SQL语句使用了SELECT *。以下脚本抓出SELECT * 的SQL:

select a.sql_id, a.sql_text, c.owner, d.table_name, d.column_cnt, c.size_mb
  from v$sql a,
       v$sql_plan b,
       (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
          from dba_segments
         group by owner, segment_name) c,
       (select owner, table_name, count(*) column_cnt
          from dba_tab_cols
         group by owner, table_name) d
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and b.object_owner = c.owner
   and b.object_name = c.segment_name
   and b.object_owner = d.owner
   and b.object_name = d.table_name
   and REGEXP_COUNT(b.projection, ']') = d.column_cnt
   and c.owner = 'SCOTT'
 order by 6 desc;

我们在Scott账户中运行如下SQL:

select * from t where object_id<1000;

我们使用脚本将其抓出:

select a.sql_id, a.sql_text, c.owner, d.table_name, d.column_cnt, c.size_mb
from v$sql a,
     v$sql_plan b,
     (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
      from dba_segments
      group by owner, segment_name) c,
     (select owner, table_name, count(*) column_cnt
      from dba_tab_cols
      group by owner, table_name) d
where a.sql_id = b.sql_id
  and a.child_number = b.child_number
  and b.object_owner = c.owner
  and b.object_name = c.segment_name
  and b.object_owner = d.owner
  and b.object_name = d.table_name
  and REGEXP_COUNT(b.projection, ']') = d.column_cnt
  and c.owner = 'SCOTT'
order by 6 desc;

在这里插入图片描述

5、抓出有标量子查询的SQL

此脚本不依赖统计信息。在开发过程中,我们应该尽量避免编写标量子查询。我们可以通过分析执行计划,抓出标量子查询语句。同一个SQL语句,执行计划中如果有两个或者两个以上的depth=1的执行计划就表示SQL中出现了标量子查询。以下脚本抓出Scott账户下在SQL*Plus中运行过的标量子查询语句:

select sql_id, sql_text, module
  from v$sql
 where parsing_schema_name = 'SCOTT'
   and module = 'SQL*Plus'
   AND sql_id in
       (select sql_id
          from (select sql_id,
                     count(*) over(partition by sql_id, child_number, depth) cnt
                  from V$SQL_PLAN
                 where depth = 1
                   and (object_owner = 'SCOTT' or object_owner is null))
         where cnt >= 2);

我们在SQL*Plus中运行如下标量子查询语句:

SQL> select dname,
  2   (select max(sal) from emp where deptno = d.deptno) max_sal
  3  from dept d;

DNAME             MAX_SAL
-------------- ----------
ACCOUNTING           5000
RESEARCH             3000
SALES                2850
OPERATIONS

我们利用以上脚本将刚运行过的标量子查询抓出:

SQL> select sql_id, sql_text, module
  2    from v$sql
  3   where parsing_schema_name = 'SCOTT'
  4     and module = 'SQL*Plus'
  5     AND sql_id in
  6         (select sql_id
  7            from (select sql_id,
  8                       count(*) over(partition by sql_id, child_number, depth) cnt
  9                    from V$SQL_PLAN
 10                   where depth = 1
 11                     and (object_owner = 'SCOTT' or object_owner is null))
 12           where cnt >= 2);

SQL_ID          SQL_TEXT                                        MODULE
--------------- ----------------------------------------------  ---------------------
739fhcu0pbz28   select dname,  (select max(sal) from emp where  SQL*Plus
                 deptno = d.deptno) max_sal from dept d

6、抓出带有自定义函数的SQL

此脚本不依赖统计信息。在开发过程中,我们应该避免在SQL语句中调用自定义函数。我们可以通过以下SQL语句抓出SQL语句中调用了自定义函数的SQL:

select distinct sql_id, sql_text, module
  from V$SQL,
       (select object_name
          from DBA_OBJECTS O
         where owner = 'SCOTT'
           and object_type in ('FUNCTION', 'PACKAGE'))
 where (instr(upper(sql_text), object_name) > 0)
   and plsql_exec_time > 0
   and regexp_like(upper(sql_fulltext), '^[SELECT]')
   and parsing_schema_name = 'SCOTT';

我们在Scott账户中创建如下函数:

create or replace function f_getdname(v_deptno in number) return varchar2 as
  v_dname dept.dname%type;
begin
  select dname into v_dname from dept where deptno = v_deptno;
  return v_dname;
end f_getdname;
/

然后我们在Scott账户中运行如下SQL:

SQL> select empno,sal,f_getdname(deptno) dname from emp;

     EMPNO        SAL DNAME
---------- ---------- -------------------------
      7369        800 RESEARCH
      7499       1600 SALES
      7521       1250 SALES
      7566       2975 RESEARCH
      7654       1250 SALES
      7698       2850 SALES
      7782       2450 ACCOUNTING
      7788       3000 RESEARCH
      7839       5000 ACCOUNTING
      7844       1500 SALES
      7876       1100 RESEARCH
      7900        950 SALES
      7902       3000 RESEARCH
      7934       1300 ACCOUNTING

我们通过脚本抓出刚执行过的SQL语句:

SQL> select distinct sql_id, sql_text, module
  2    from V$SQL,
  3         (select object_name
  4            from DBA_OBJECTS O
  5           where owner = 'SCOTT'
  6             and object_type in ('FUNCTION', 'PACKAGE'))
  7   where (instr(upper(sql_text), object_name) > 0)
  8     and plsql_exec_time > 0
  9     and regexp_like(upper(sql_fulltext), '^[SELECT]')
 10     and parsing_schema_name = 'SCOTT';

SQL_ID          SQL_TEXT                                                MODULE
--------------- ------------------------------------------------------- ---------
2ck71xc69j49u   select empno,sal,f_getdname(deptno) dname from emp      SQL*Plus

7、抓出表被多次反复调用SQL

此脚本不依赖统计信息。在开发过程中,我们应该避免在同一个SQL语句中对同一个表多次访问。我们可以通过下面SQL抓出同一个SQL语句中对某个表进行多次扫描的SQL:

select a.parsing_schema_name schema,
       a.sql_id,
       a.sql_text,
       b.object_name,
       b.cnt
  from v$sql a,
       (select *
          from (select sql_id,
                       child_number,
                       object_owner,
                       object_name,
                       object_type,
                       count(*) cnt
                  from v$sql_plan
                 where object_owner = 'SCOTT'
                 group by sql_id,
                          child_number,
                          object_owner,
                          object_name,
                          object_type)
         where cnt >= 2) b
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number;

我们在Scott账户中运行如下SQL:

select ename,job,deptno from emp where sal>(select avg(sal) from emp);

以上SQL访问了emp表两次,我们可以通过脚本将其抓出:

SQL> select a.parsing_schema_name schema,
  2         a.sql_id,
  3         a.sql_text,
  4         b.object_name,
  5         b.cnt
  6    from v$sql a,
  7         (select *
  8            from (select sql_id,
  9                         child_number,
 10                         object_owner,
 11                         object_name,
 12                         object_type,
 13                         count(*) cnt
 14                    from v$sql_plan
 15                   where object_owner = 'SCOTT'
 16                   group by sql_id,
 17                            child_number,
 18                            object_owner,
 19                            object_name,
 20                            object_type)
 21           where cnt >= 2) b
 22   where a.sql_id = b.sql_id
 23     and a.child_number = b.child_number;

SCHEMA          SQL_ID          SQL_TEXT                      OBJECT_NAME         CNT
--------------- --------------- ----------------------------- ------------ ----------
SCOTT           fdt0z70z43vgv   select ename,job,deptno from  EMP                  2
                                emp where sal>(select avg(sal)
                                 from emp)

8、抓出走了FILTER的SQL

此脚本不依赖统计信息。当where子查询没能unnest,执行计划中就会出现FILTER,对于此类SQL,我们应该在上线之前对其进行改写,避免执行计划中出现FILTER,以下脚本可以抓出where子查询没能unnest的SQL:

select parsing_schema_name schema, sql_id, sql_text
  from v$sql
 where parsing_schema_name = 'SCOTT'
   and (sql_id, child_number) in
       (select sql_id, child_number
          from v$sql_plan
         where operation = 'FILTER'
           and filter_predicates like '%IS NOT NULL%'
        minus
        select sql_id, child_number
          from v$sql_plan
         where object_owner = 'SYS');

9、抓出返回行数较多的嵌套循环SQL

此脚本不依赖统计信息。两表关联返回少量数据应该走嵌套循环,如果返回大量数据,应该走HASH连接,或者是排序合并连接。如果一个SQL语句返回行数较多(大于1万行),SQL的执行计划在最后几步(Id<=5)走了嵌套循环,我们可以判定该执行计划中的嵌套循环是有问题的,应该走HASH连接。以下脚本抓出返回行数较多的嵌套循环SQL:

select *
  from (select parsing_schema_name schema,
               sql_id,
               sql_text,
               rows_processed / executions rows_processed
          from v$sql
         where parsing_schema_name = 'SCOTT'
           and executions > 0
           and rows_processed / executions > 10000
         order by 4 desc) a
 where a.sql_id in (select sql_id
                      from v$sql_plan
                     where operation like '%NESTED LOOPS%'
                       and id <= 5);

10、抓出NL被驱动表走了全表扫描的SQL

此脚本不依赖统计信息。嵌套循环的被驱动表应该走索引,以下脚本抓出嵌套循环被驱动表走了全表扫描的SQL,同时根据表大小降序显示:

select c.sql_text, a.sql_id, b.object_name, d.mb
  from v$sql_plan a,
       (select *
          from (select sql_id,
                       child_number,
                       object_owner,
                       object_name,
                       parent_id,
                       operation,
                       options,
                       row_number() over(partition by sql_id, child_number, parent_id order by id) rn
                  from v$sql_plan)
         where rn = 2) b,
       v$sql c,
       (select owner, segment_name, sum(bytes / 1024 / 1024) mb
          from dba_segments
         group by owner, segment_name) d
 where b.sql_id = c.sql_id
   and b.child_number = c.child_number
   and b.object_owner = 'SCOTT'
   and a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and a.operation like '%NESTED LOOPS%'
   and a.id = b.parent_id
   and b.operation = 'TABLE ACCESS'
   and b.options = 'FULL'
   and b.object_owner = d.owner
   and b.object_name = d.segment_name
 order by 4 desc;

11、抓出走了TABLE ACCESS FULL的SQL

此脚本不依赖统计信息。如果一个大表走了全表扫描,会严重影响SQL性能。这时我们可以查看大表与谁进行关联。如果大表与小表(小结果集)关联,我们可以考虑让大表作为嵌套循环被驱动表,大表走连接列索引。如果大表与大表(大结果集)关联,我们可以检查大表过滤条件是否可以走索引,也要检查大表被访问了多少个字段。假设大表有50个字段,但是只访问了其中5个字段,这时我们可以建立一个组合索引,将where过滤字段、表连接字段以及select访问的字段组合在一起,这样就可以直接从索引中获取数据,避免大表全表扫描,从而提升性能。下面脚本抓出走了全表扫描的SQL,同时显示访问了表多少个字段,表一共有多少个字段以及表段大小:

select a.sql_id,
       a.sql_text,
       d.table_name,
       REGEXP_COUNT(b.projection, ']') ||'/'|| d.column_cnt  column_cnt,
       c.size_mb,
       b.FILTER_PREDICATES filter
  from v$sql a,
       v$sql_plan b,
       (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
          from dba_segments
         group by owner, segment_name) c,
       (select owner, table_name, count(*) column_cnt
          from dba_tab_cols
         group by owner, table_name) d
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and b.object_owner = c.owner
   and b.object_name = c.segment_name
   and b.object_owner = d.owner
   and b.object_name = d.table_name
   and c.owner = 'SCOTT'
   and b.operation = 'TABLE ACCESS'
   and b.options = 'FULL'
 order by 5 desc;

12、抓出走了INDEX FULL SCAN的SQL

此脚本不依赖统计信息。INDEX FULL SCAN会扫描索引中所有的叶子块,单块读。如果索引很大,执行计划中出现了INDEX FULL SCAN,这时SQL会出现严重的性能问题,因此我们需要抓出走了INDEX FULL SCAN的SQL。以下脚本抓出走了INDEX FULL SCAN的SQL并且根据索引段大小降序显示:

select c.sql_text, c.sql_id, b.object_name, d.mb
  from v$sql_plan b,
       v$sql c,
       (select owner, segment_name, sum(bytes / 1024 / 1024) mb
          from dba_segments
         group by owner, segment_name) d
 where b.sql_id = c.sql_id
   and b.child_number = c.child_number
   and b.object_owner = 'SCOTT'
   and b.operation = 'INDEX'
   and b.options = 'FULL SCAN'
   and b.object_owner = d.owner
   and b.object_name = d.segment_name
 order by 4 desc;

13、抓出走了INDEX SKIP SCAN的SQL

此脚本不依赖统计信息。当执行计划中出现了INDEX SKIP SCAN,通常说明需要额外添加一个索引。以下脚本抓出走了INDEX SKIP SCAN的SQL:

select c.sql_text, c.sql_id, b.object_name, d.mb
  from v$sql_plan b,
       v$sql c,
       (select owner, segment_name, sum(bytes / 1024 / 1024) mb
          from dba_segments
         group by owner, segment_name) d
 where b.sql_id = c.sql_id
   and b.child_number = c.child_number
   and b.object_owner = 'SCOTT'
   and b.operation = 'INDEX'
   and b.options = 'SKIP SCAN'
   and b.object_owner = d.owner
   and b.object_name = d.segment_name
 order by 4 desc;

14、抓出索引被哪些SQL引用

此脚本不依赖统计信息。有时开发人员可能会胡乱建立一些索引,但是这些索引在数据库中可能并不会被任何一个SQL使用。这样的索引会增加维护成本,我们可以将其删掉。下面脚本查询SQL使用哪些索引:

select a.sql_text, a.sql_id, b.object_owner, b.object_name, b.object_type
  from v$sql a, v$sql_plan b
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and object_owner = 'SCOTT'
   and object_type like '%INDEX%'
order by 3,4,5;

15、 抓出走了笛卡儿积的SQL

当两表没有关联条件的时候就会走笛卡儿积,当Rows被估算为1的时候,也可能走笛卡儿积连接。下面脚本抓出走了笛卡儿积的SQL:

select c.sql_text,
       a.sql_id,
       b.object_name,
       a.filter_predicates filter,
       a.access_predicates predicate,
       d.mb
  from v$sql_plan a,
       (select *
          from (select sql_id,
                       child_number,
                       object_owner,
                       object_name,
                       parent_id,
                       operation,
                       options,
                       row_number() over(partition by sql_id, child_number, parent_id order by id) rn
                  from v$sql_plan)
         where rn = 1) b,
       v$sql c,
       (select owner, segment_name, sum(bytes / 1024 / 1024) mb
          from dba_segments
         group by owner, segment_name) d
 where b.sql_id = c.sql_id
   and b.child_number = c.child_number
   and b.object_owner = 'SCOTT'
   and a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and a.operation = 'MERGE JOIN'
   and a.id = b.parent_id
   and a.options = 'CARTESIAN'
   and b.object_owner = d.owner
   and b.object_name = d.segment_name
 order by 4 desc;

16、抓出走了错误的排序合并连接的SQL

此脚本不依赖统计信息。排序合并连接一般用于非等值关联,如果两表是等值关联,我们建议使用HASH连接代替排序合并连接,因为HASH连接只需要将驱动表放入PGA中,而排序合并连接要么是将两个表放入PGA中,要么是将一个表放入PGA中、另外一个表走INDEX FULL SCAN,然后回表。如果两表是等值关联并且两表比较大,这时应该走HASH连接而不是排序合并连接。下面脚本抓出两表等值关联但是走了排序合并连接的SQL,同时显示离MERGE JOIN关键字较远的表的段大小(太大PGA放不下):

select c.sql_id, c.sql_text, d.owner, d.segment_name, d.mb
  from v$sql_plan a,
       v$sql_plan b,
       v$sql c,
       (select owner, segment_name, sum(bytes / 1024 / 1024) mb
          from dba_segments
         group by owner, segment_name) d
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and b.operation = 'SORT'
   and b.options = 'JOIN'
   and b.access_predicates like '%"="%'
   and a.parent_id = b.id
   and a.object_owner = 'SCOTT'
   and b.sql_id = c.sql_id
   and b.child_number = c.child_number
   and a.object_owner = d.owner
   and a.object_name = d.segment_name
 order by 4 desc;

17、抓出LOOP套LOOP的PSQL

此脚本不依赖统计信息。在编写PLSQL的时候,我们应该尽量避免LOOP套LOOP,因为双层循环,最内层循环类似笛卡儿积。假设外层循环返回1 000行数据,内层循环返回1 000行数据,那么内层循环里面的代码就会执行1000*1000次。以下脚本可以抓出LOOP套LOOP的PLSQL:

with x as
(select / *+ materialize */ owner,name,type,line,text,rownum rn from dba_source where (upper(text) like '%END%LOOP%' or upper(text) like '%FOR%LOOP%'))
select a.owner,a.name,a.type from x a,x b
where ((upper(a.text) like '%END%LOOP%'
and upper(b.text) like '%END%LOOP%'
and a.rn+1=b.rn)
or (upper(a.text) like '%FOR%LOOP%'
and upper(b.text) like '%FOR%LOOP%'
and a.rn+1=b.rn))
and a.owner=b.owner
and a.name=b.name
and a.type=b.type
and a.owner='SCOTT';

18、抓出走了低选择性索引的SQL

此脚本依赖统计信息。如果一个索引选择性很低,说明列数据分布不均衡。当SQL走了数据分布不均衡列的索引,很容易走错执行计划,此时我们应该检查SQL语句中是否有其他过滤条件,如果有其他过滤条件,可以考虑建立组合索引,将选择性高的列作为引导列;如果没有其他过滤条件,应该检查列是否有收集直方图。以下脚本抓出走了低选择性索引的SQL:

select c.sql_id,
       c.sql_text,
       b.index_name,
       e.table_name,
       trunc(d.num_distinct / e.num_rows * 100, 2) selectivity,
       d.num_distinct,
       e.num_rows
  from v$sql_plan a,
       (select *
          from (select index_owner,
                       index_name,
                       table_owner,
                       table_name,
                       column_name,
                       count(*) over(partition by index_owner, index_name, table_owner, table_name) cnt
                  from dba_ind_columns)
         where cnt = 1) b,
       v$sql c,
       dba_tab_col_statistics d,
       dba_tables e
 where a.object_owner = b.index_owner
   and a.object_name = b.index_name
   and b.index_owner = 'SCOTT'
   and a.access_predicates is not null
   and a.sql_id = c.sql_id
   and a.child_number = c.child_number
   and d.owner = e.owner
   and d.table_name = e.table_name
   and b.table_owner = e.owner
   and b.table_name = e.table_name
   and d.column_name = b.column_name
   and d.table_name = b.table_name
   and d.num_distinct / e.num_rows < 0.1;

19、抓出可以创建组合索引的SQL(回表再过滤选择性高的列)

回表次数太多会严重影响SQL性能。当执行计划中发生了回表再过滤并且过滤字段的选择性比较高,我们可以将过滤字段包含在索引中避免回表再过滤,从而减少回表次数,提升查询性能。以下脚本抓出回表再过滤选择性较高的列:

select a.sql_id,
       a.sql_text,
       f.table_name,
       c.size_mb,
       e.column_name,
       round(e.num_distinct / f.num_rows * 100, 2) selectivity
  from v$sql a,
       v$sql_plan b,
       (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
          from dba_segments
         group by owner, segment_name) c,
       dba_tab_col_statistics e,
       dba_tables f
 where a.sql_id = b.sql_id
   and a.child_number = b.child_number
   and b.object_owner = c.owner
   and b.object_name = c.segment_name
   and e.owner = f.owner
   and e.table_name = f.table_name
   and b.object_owner = f.owner
   and b.object_name = f.table_name
   and instr(b.filter_predicates, e.column_name) > 0
   and (e.num_distinct / f.num_rows) > 0.1
   and c.owner = 'SCOTT'
   and b.operation = 'TABLE ACCESS'
   and b.options = 'BY INDEX ROWID'
   and e.owner = 'SCOTT'
 order by 4 desc;

20、抓出可以创建组合索引的SQL(回表只访问少数字段)

此脚本不依赖统计信息。我们在第1章中讲到,回表次数太多会严重影响SQL性能。当SQL走索引回表只访问表中少部分字段,我们可以将这些字段与过滤条件组合起来建立为一个组合索引,这样就能避免回表,从而提升查询性能。下面脚本抓出回表只访问少数字段的SQL:

select a.sql_id,a.sql_text,d.table_name,REGEXP_COUNT(b.projection, ']') ||'/'|| d.column_cntcolumn_cnt,c.size_mb,...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/564045.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3的getCurrentInstance获取当前组件实例

vue3的setup中没有this时需要使用getCurrentInstance()来获取。 在 Vue 3 中&#xff0c;getCurrentInstance 方法可以在组合式 API&#xff08;Composition API&#xff09;中获取当前组件实例。这个方法返回一个包含了组件实例的对象&#xff0c;你可以用它来访问组件的 pro…

【刷题】代码随想录算法训练营第二十天|654、最大二叉树,617、合并二叉树,700、二叉搜索树中的搜索,98、验证二叉搜索树

目录 654、最大二叉树617、合并二叉树700、二叉搜索树中的搜索98、验证二叉搜索树 654、最大二叉树 讲解&#xff1a;https://programmercarl.com/0654.%E6%9C%80%E5%A4%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html 最大二叉树的规则&#xff1a; 二叉树的根是数组中的最大元素。左子…

电商数据采集API接口系列|请求示例测试方式丨商品详情,详情图,sku价格等

电商数据采集API接口系列是用于从电商平台收集各种商品信息的工具&#xff0c;包括商品详情、详情图、SKU价格等。以下是一般情况下使用电商API接口进行数据采集的步骤和测试方式&#xff1a; 1.请求方式&#xff1a;HTTP POST GET &#xff08;复制薇&#xff1a;Anzexi58 获…

VS安装教程

文章目录 VS安装步骤 VS安装步骤 &#xff08;1&#xff09; 下载VS2022社区版&#xff08;根据情况选择自己需要的版本下载&#xff09;&#xff0c;下载的方式&#xff0c;可以通过微软官方下载。https://visualstudio.microsoft.com/zh-hans/downloads/?cidlearn-onpage-d…

uniapp——授权报错,选择合适的基础库

说明 我的小程序开发版本点击选择头像报错 更换基础库就好了

4.9 启动系统任务❤❤❤

有一些特殊的任务需要在系统启动时执行&#xff0c;例如配置文件加载、数据库初始化等操作。 Spring Boot对此提供了两种解决方案&#xff1a;CommandLineRunner和ApplicationRunner。 CommandLineRunner和ApplicationRunner基本一致&#xff0c;差别主要体现在参数上。 1. Co…

vue详解(3)

1. Vue 生命周期总结 四个阶段&#xff0c;八个钩子 -> 三个常用 created&#xff0c;mounted&#xff0c;beforeDestroy 2. 工程化开发 & 脚手架 Vue CLI 基本介绍&#xff1a; Vue CLI 是 Vue 官方提供的一个全局命令工具。 可以帮助我们快速创建一个开发 Vue 项目…

基于深度学习的脑部肿瘤检测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 当大脑中形成异常细胞时&#xff0c;就会发生脑肿瘤。肿瘤主要有两种类型&#xff1a;癌性&#xff08;恶性&#xff09;肿瘤和良性肿瘤。恶性肿瘤可分为原发性肿瘤和继发性肿瘤&#xff0c;前者始…

单片机STM32中断与事件的区别

【转】1-单片机STM32---中断与事件的区别 - Engraver - 博客园 (cnblogs.com) 路径不同&#xff0c;处理方式不同&#xff0c;是否有程序不同&#xff0c;是否有cpu参与不同。 事件是比中断更新的升级产物。

Golang | Leetcode Golang题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; func firstMissingPositive(nums []int) int {n : len(nums)for i : 0; i < n; i {for nums[i] > 0 && nums[i] < n && nums[nums[i]-1] ! nums[i] {nums[nums[i]-1], nums[i] nums[i], nums[nums[i]-1]}}for i …

JavaScript变量及数据类型

目录 概述&#xff1a; 变量&#xff1a; 前言&#xff1a; 变量的命名&#xff1a; 定义变量&#xff1a; 为变量赋值&#xff1a; 变量提升&#xff1a; let和const关键字&#xff1a; JS数据类型&#xff1a; 前言&#xff1a; typeof操作符&#xff1a; JS基本…

一二三应用开发平台使用手册——系统管理-用户组-使用说明

概述 在RBAC模型中&#xff0c;资源、角色、用户三个关键元素&#xff0c;构成权限体系。在平台设计和实现的时候&#xff0c;以下几个核心问题思考如下&#xff1a; 角色&#xff0c;单层平铺还是树形结构&#xff1f; 在小型应用中&#xff0c;角色数量有限的情况下&#x…

Gitea 简单介绍、用法以及使用注意事项!

Gitea 是一个轻量级的代码托管解决方案&#xff0c;它提供了一个简单而强大的平台&#xff0c;用于托管和协作开发项目。基于 Go 语言编写&#xff0c;与 GitLab 和 GitHub Enterprise 类似&#xff0c;但专为自托管而设计。以下是对 Gitea 的详细介绍&#xff0c;包括常用命令…

SpringBoot---------@Value,@ConfigurationProperyies以及多环境开发配置

application.yml server:port: 8080#logging: # level: # root: info(默认)array:name: zzlage: 18subject:- Java- Vue- SpringBoot Value注解的使用:从yml配置文件中获取数据&#xff0c;即可在类中使用 Value("${server.port}")private String port;Value(&q…

SpringBoot中Bean的创建过程及扩展操作点 @by_TWJ

目录 1. 类含义2. Bean创建过程 - 流程图3. 例子3.1. 可变属性注入到实体中3.2. 模拟Bean创建的例子 1. 类含义 BeanDefinition - 类定义&#xff0c;为Bean创建提供一些定义类的信息。实现类如下&#xff1a; RootBeanDefinition - 类定义信息&#xff0c;包含有父子关系的Be…

美国RAKSmart服务器性能科普

当我们谈论服务器性能时&#xff0c;无疑会涉及多个维度&#xff0c;包括存储能力、网络连接、稳定性、管理界面以及安全性等。在这篇科普文章中&#xff0c;我们将深入探讨美国RAKSmart服务器的性能特点。 首先&#xff0c;RAKSmart服务器在存储和扩展性方面表现出色。它们配备…

Git 工作原理

Git 工作原理 | CoderMast编程桅杆https://www.codermast.com/dev-tools/git/git-workspace-index-repo.html Workspace&#xff1a;工作区Index / Stage&#xff1a;暂存区Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09;Remote&#xff1a;远程仓库 Git 一…

绿联 安装transmission

绿联 安装transmission及中文UI 1、镜像 linuxserver/transmission:latest 2、安装 2.1、创建容器 按需配置权重。 2.2、基础设置 2.3、网络 桥接即可。 注&#xff1a;如果使用IPV6&#xff0c;请选择"host"模式。 注&#xff1a;如果使用IPV6&#xff0c;请选…

网络数据包嗅探器工具

组织的网络非常庞大&#xff0c;包含服务器、交换机、路由器和接入点等众多节点&#xff0c;由于许多资源和流量不断通过这些节点&#xff0c;因此很难确定大量流量是真实的还是安全攻击的迹象&#xff0c;了解和了解组织的网络流量至关重要&#xff0c;一个有用的资源是网络数…

JAVA学习笔记31(IO流)

1.IO流 1.文件流 ​ *文件在程序中是以流的形式来操作的 2.常用文件操作 1.创建文件对象 1.new File(String pathname) //根据路径构建一个File对象 main() {}public void create01() {String filePath "e:\\news1.txt";File filePath new File(filePath);tr…
最新文章