To find foreign keys that don't have corresponding indexes

Locks and Foreign Keys


Oracle Database maximizes the concurrency control of parent keys in relation to dependent foreign keys. In heap-organized tables, locking behavior depends on the indexing of foreign key columns. If foreign keys are not indexed, then the child table will probably be locked more frequently, deadlocks will occur, and concurrency will be decreased. For this reason, Oracle recommends indexing foreign keys in most cases except when the matching unique or primary key is never updated or deleted. -link

vi check_fk_witout_index.sql
note: you must be connect as schema
note: The owner is pick up from schema has been connect
col TABLE_NAME format a30
col CONSTRAINT_NAME format a30
select uc.table_name, uc.constraint_name
from user_constraints uc
where uc.constraint_type='R'
and exists
(select ucc.position, ucc.column_name
from user_cons_columns ucc
where ucc.constraint_name=uc.constraint_name
minus
select uic.column_position as position, uic.column_name
from user_ind_columns uic
where uic.table_name=uc.table_name);

vi check_fk_witout_index_type_schema_name.sql
note: check in dba views, you have to tape owner

col TABLE_NAME format a30
col CONSTRAINT_NAME format a30
select uc.table_name, uc.constraint_name
from dba_constraints uc
where uc.owner='&owner' and uc.constraint_type='R'
and exists
(select ucc.position, ucc.column_name
from dba_cons_columns ucc
where ucc.constraint_name=uc.constraint_name and ucc.owner=uc.owner
minus
select uic.column_position as position, uic.column_name
from dba_ind_columns uic
where uic.table_name=uc.table_name and uic.table_owner=uc.owner);

To see constraints within schema
select TABLE_NAME, R_OWNER, R_CONSTRAINT_NAME, CONSTRAINT_TYPE, DELETE_RULE from dba_constraints where owner='SCOTT';

TABLE_NAME                     R_OWNER              R_CONSTRAINT_NAME              C DELETE_RU
------------------------------ -------------------- ------------------------------ - ---------
EMP                            SCOTT                PK_DEPT                        R NO ACTION
EMP                                                                                P

DEPT                                                                               P

To see index schema 
select TABLE_OWNER, TABLE_NAME, INDEX_NAME from dba_indexes where OWNER='SCOTT'

TABLE_OWNER                    TABLE_NAME                     INDEX_NAME
------------------------------ ------------------------------ ------------------------------
SCOTT                          EMP                            PK_EMP
SCOTT                          DEPT                           PK_DEPT

To see columns used  by a specified index
select COLUMN_NAME from dba_ind_columns where INDEX_OWNER='SCOTT' and INDEX_NAME='PK_DEPT';

COLUMN_NAME
--------------------------------------------------------------------------------

DEPTNO

To create index
create index scott.fk_emp__dept on scott.emp(DEPTNO);

Report
note: I have to test this script
select a.owner            "Owner",
                                 a.table_name       "Table_Name",
                                 a.constraint_name  "Constraint_Name",
                                 a.columns          "Foreign_Key_Column_1",
                                 b.columns          "Foreign_Key_Column_2",
                                 a.owner             sdev_link_owner,
                                 a.table_name        sdev_link_name,
                                 'TABLE'             sdev_link_type
                            from 
                            ( select a.owner, substr(a.table_name,1,30) table_name, 
                                 substr(a.constraint_name,1,30) constraint_name, 
                                   max(decode(position, 1,     substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 2,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 3,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 4,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 5,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 6,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 7,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 8,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position, 9,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,10,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,11,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,12,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,13,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,14,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,15,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(position,16,', '||substr(column_name,1,30),NULL)) columns
                                from sys.dba_cons_columns a,
                                     sys.dba_constraints b
                               where a.constraint_name = b.constraint_name
                                 and a.owner = b.owner
                                 and (:OWNER is null or instr(b.owner, upper(:OWNER))>0)
                                 and b.constraint_type = 'R'
                               group by a.owner, substr(a.table_name,1,30), substr(a.constraint_name,1,30) ) a, 
                            ( select table_owner, 
                                     substr(table_name,1,30) table_name, substr(index_name,1,30) index_name, 
                                   max(decode(column_position, 1,substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 2,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 3,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 4,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 5,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 6,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 7,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 8,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position, 9,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,10,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,11,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,12,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,13,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,14,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,15,', '||substr(column_name,1,30),NULL)) || 
                                   max(decode(column_position,16,', '||substr(column_name,1,30),NULL)) columns
                                from sys.dba_ind_columns 
                               group by table_owner, substr(table_name,1,30), substr(index_name,1,30) ) b
                      where a.owner      = b.table_owner (+)
                        and a.table_name = b.table_name (+)
                        and substr(a.table_name,1,4) != 'BIN$'
                        and substr(a.table_name,1,3) != 'DR$'
                        and b.table_name is null
                        and b.columns (+) like a.columns || '%'

                      order by a.owner, a.table_name, a.constraint_name;