zoukankan      html  css  js  c++  java
  • 抓出外键没创建索引的表

    create table parent_tb_test 
    (
        id         number(10),
        name    varchar2(32),
        constraint pk_parent_tb_test primary key(id)
    );
    
    create table child_tb_test
    (
         c_id   number(10),
         f_id        number(10),
         child_name    varchar2(32),
         constraint pk_child_tb_test primary key(c_id),
         foreign key(f_id) references parent_tb_test
    );
    
    
    SQL> select * from user_tables;
    
    no rows selected
    
    SQL> select * from user_indexes;
    
    no rows selected
    
    
    SQL> select table_name from user_tables;
    
    TABLE_NAME
    ------------------------------
    CHILD_TB_TEST
    PARENT_TB_TEST
    
    SQL> select index_name from user_indexes;
    
    INDEX_NAME
    ------------------------------
    PK_CHILD_TB_TEST
    PK_PARENT_TB_TEST
    
    在ORACLE数据库中,定义外键约束时,ORACLE是不会自动创建对应索引的,必须手动在外键约束相关的列上创建索引。那么外键字段上是否有必要创建索引呢?如果有必要的话,巡检时,如何找出外键字段上没有创建索引的相关表,并生成对应的索引的脚本呢?
    
    外键缺失索引影响
    
    外键列上缺少索引会带来三个问题,限制并发性、影响性能、还有可能造成死锁。所以对于绝大部分场景,我们应该尽量考虑在外键上面创建索引
    
    影响性能。 如果子表外键没有创建索引,那么当父表查询关联子表时,子表将进行全表扫描。影响表连接方式。
    
    影响并发。 无论是更新父表主键,或者删除一个父记录,都会在子表中加一个表锁(在这条语句完成前,不允许对子表做任何修改)。这就会不必要
    
    地锁定更多的行,而影响并发性
    
             3:在特殊情况下,还有可能造成死锁。
    
    我们先来看看一个简单的例子,看看当外键缺失索引时,子表是否进行全表扫描,如下所示,表EMP与DEPT存在主外键关系:
    
    begin
    
    for index_num in 1 .. 10000 loop
        insert into parent_tb_test
        select index_num , 'kerry' || to_char(index_num) from dual;
    
        if mod(index_num,100) = 0 then
            commit;
        end if;
    end loop;
    
         commit;
    
    end;
    /
    
    declare index_num number :=1;
    begin
    
        for index_parent  in 1 .. 10000 loop
            for index_child in 1 .. 1000 loop
                 insert into child_tb_test
                 select index_num, index_parent, 'child' || to_char(index_child) from dual;
    
                 index_num := index_num +1;
                 if mod(index_child,1000) = 0 then
                     commit;
                 end if;
            end loop;
        end loop;
    
        commit;
    end;
    /
    
    
     BEGIN
          DBMS_STATS.GATHER_TABLE_STATS(ownname          => 'PARENT_TB_TEST',
                                        tabname          => 'TEST',
                                       estimate_percent => 100,
                                       method_opt       => 'for all columns size 1',
                                     no_invalidate    => FALSE,
                                       degree           => 1,
                                        cascade          => TRUE);
        END;
    	
    	
    上面脚本构造了测试用的例子和数据, 那么我们对比看看外键有无索引的区别:
    
    ---高级执行计划:
    11G:
    alter session set statistics_level=all; ---再运行SQL
    
    select p.id, p.name, c.child_name
      from test.parent_tb_test p
     inner join test.child_tb_test c
        on p.id = c.f_id
     where p.id = 1000;
    
     
    select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
    
    
    SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    SQL_ID	dzm01p60dbuva, child number 0
    -------------------------------------
    select p.id, p.name, c.child_name   from test.parent_tb_test p	inner
    join test.child_tb_test c     on p.id = c.f_id	where p.id = 1000
    
    Plan hash value: 901213199
    
    ---------------------------------------------------------------------------------------------------------------------
    | Id  | Operation		     | Name		 | Starts | E-Rows | A-Rows |	A-Time	 | Buffers | Reads  |
    ---------------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT	     |			 |	1 |	   |   1000 |00:00:00.28 |   32875 |  32793 |
    |   1 |  NESTED LOOPS		     |			 |	1 |   1000 |   1000 |00:00:00.28 |   32875 |  32793 |
    |   2 |   TABLE ACCESS BY INDEX ROWID| PARENT_TB_TEST	 |	1 |	 1 |	  1 |00:00:00.01 |	 3 |	  0 |
    |*  3 |    INDEX UNIQUE SCAN	     | PK_PARENT_TB_TEST |	1 |	 1 |	  1 |00:00:00.01 |	 2 |	  0 |
    |*  4 |   TABLE ACCESS FULL	     | CHILD_TB_TEST	 |	1 |   1000 |   1000 |00:00:00.28 |   32872 |  32793 |
    ---------------------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       3 - access("P"."ID"=1000)
       4 - filter("C"."F_ID"=1000)
    
    
    23 rows selected.
    
    
    此时走全表扫描;
    
    SQL> with cons as (select /*+ materialize */ owner, table_name, constraint_name
              from dba_constraints
             where owner = 'TEST'
               AND constraint_type = 'R'),
         idx as (
         select /*+ materialize */ table_owner,table_name, column_name
              from dba_ind_columns
             where table_owner = 'TEST')      
    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);  2    3    4    5    6    7    8    9   10   11   12   13   14  
    
    OWNER			       TABLE_NAME		      CONSTRAINT_NAME		     COLUMN_NAME
    ------------------------------ ------------------------------ ------------------------------ ------------------------------
    TEST			       CHILD_TB_TEST		      SYS_C0011365		     F_ID
    
    
    接下来,我们再来看看外键缺失索引影响并发,以及造成死锁的情况,如下所示,创建表dead_lock_parent与dead_lock_foreign,两者存在主外键关系,分布插入两条测试数据:
    
    
    SQL> create table dead_lock_parent( id number primary key, name varchar2(32));
    
    Table created.
    
    SQL> create table dead_lock_foreign(fid  number, fname varchar2(32), foreign key(fid) references dead_lock_parent);
    
    Table created.
    
    SQL> insert into dead_lock_parent values( 1, 'kerry');
    
    1 row created.
    
    SQL> insert into dead_lock_foreign values(1, 'kerry_fk');  
    
    1 row created.
    
    SQL> insert into dead_lock_parent values(2, 'jimmy');
    
    1 row created.
    
    SQL> insert into dead_lock_foreign values(2, 'jimmy_fk');
    
    1 row created.
    
    SQL> commit;
    
    Commit complete.
    
    SQL> 
    
    SQL> set linesize 200
    SQL> set pagesize 200
    SQL> select * from v$mystat where rownum=1;
    
           SID STATISTIC#	   VALUE
    ---------- ---------- ----------
           133	    0	       0
    
    SQL> select * from dead_lock_foreign;
    
           FID FNAME
    ---------- --------------------------------
    	 1 kerry_fk
    	 2 jimmy_fk
    
    SQL> delete from dead_lock_foreign where fid=1;
    
    1 row deleted.
    
    
    
    
    
    SQL> set linesize 200
    SQL> set pagesize 200
    SQL> select * from v$mystat where rownum=1;
    
           SID STATISTIC#	   VALUE
    ---------- ---------- ----------
           195	    0	       0
    
    SQL> delete from dead_lock_foreign where fid=2;
    
    1 row deleted.
    
    
    
    接着在会话1-133上,执行里执行删除dead_lock_parent中id为1的记录:
    
     delete from dead_lock_parent where id=1;
    
    SQL> select * from v$mystat where rownum=1;
    
           SID STATISTIC#	   VALUE
    ---------- ---------- ----------
           133	    0	       0
    
    SQL> select * from dead_lock_foreign;
    
           FID FNAME
    ---------- --------------------------------
    	 1 kerry_fk
    	 2 jimmy_fk
    
    SQL> delete from dead_lock_foreign where fid=1;
    
    1 row deleted.
    
    SQL>  delete from dead_lock_parent where id=1;
    
    
    
       	SESSION_ID	SQL_ID	MACHINE	BLOCKING_SESSION	SAMPLE_TIME	MODULE	PROGRAM	EVENT	SQL_TEXT
    1	133	027sq2bwan3mv	node2	195	24-9月 -19 07.43.19.848 下午	SQL*Plus	sqlplus@node2 (TNS V1-V3)	enq: TM - contention	 delete from dead_lock_parent where id=1
    2	133	027sq2bwan3mv	node2	195	24-9月 -19 07.43.18.848 下午	SQL*Plus	sqlplus@node2 (TNS V1-V3)	enq: TM - contention	 delete from dead_lock_parent where id=1
    3	133	027sq2bwan3mv	node2	195	24-9月 -19 07.43.17.848 下午	SQL*Plus	sqlplus@node2 (TNS V1-V3)	enq: TM - contention	 delete from dead_lock_parent where id=1
    
    
    133 被195 堵塞:
    
    
    133执行的sql:
    SQL> select  sql_text from v$sqlarea where (address,hash_value) in 
    (select  /*+unnest*/ DECODE(sql_hash_value,0,prev_sql_addr,sql_address),DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value) from 
    v$session where sid =133)  2    3  ;
    
    SQL_TEXT
    --------------------------------------------------------------------------------
     delete from dead_lock_parent where id=1
     
     
     
     195 执行的SQL:
     SQL> select  sql_text from v$sqlarea where (address,hash_value) in 
    (select  /*+unnest*/ DECODE(sql_hash_value,0,prev_sql_addr,sql_address),DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value) from 
    v$session where sid =195)  2    3  
      4  ;
    
    SQL_TEXT
    --------------------------------------------------------------------------------
    delete from dead_lock_foreign where fid=2
    
    
    
  • 相关阅读:
    React 生命周期
    React 总结
    系统后台设置
    数据库的事务日志已满,起因为"LOG_BACKUP"。
    webpack 打包器
    地图API
    ES6 与 React
    前端流行的技术
    Javascript 函数声明、函数表达式与匿名函数自执行表达式
    Javascript 解读与思想
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13348599.html
Copyright © 2011-2022 走看看