日期:2014-05-16 浏览次数:20496 次
主外键有两大特点:
1)主键本身是一个唯一索引,保证主键所在列的唯一性;
2)外键列指必须在主表中的主键列有相应记录。
我们知道,主键本身是一个唯一索引,外键是一个约束,默认情况下没有索引,但在实际使用中强烈建议在外键上建索引,下面看两个例子:
SQL> alter database mount; Database altered. SQL> alter database open; Database altered. SQL> create table p (id number, name varchar2(30)); Table created. SQL> alter table p add constraint pk primary key(id); Table altered. SQL> create table f (id number, pid number, name varchar2(30)); Table created. SQL> alter table f add constraint fk foreign key(pid) references p(id); Table altered. SQL> insert into p select rownum,table_name from dba_tables; 1206 rows created. SQL> insert into f select rownum,mod(rownum,1000)+1,object_name from dba_objects; 14090 rows created. SQL> commit; Commit complete.上面语句分别创建了两个表,其中表p有主键,另一个表f有外键。但我们没有在外键上创建索引,下面我们看下这两表关联的执行计划和性能:
SQL> set autotrace traceonly SQL> set line 1000 SQL> select p.id,p.name,f.name from p,f where p.id=f.pid and p.id=880; 14 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 3936432439 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | 840 | 19 (0)| 00:00:01 | | 1 | NESTED LOOPS | | 14 | 840 | 19 (0)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| P | 1 | 30 | 0 (0)| 00:00:01 | |* 3 | INDEX UNIQUE SCAN | PK | 1 | | 0 (0)| 00:00:01 | |* 4 | TABLE ACCESS FULL | F | 14 | 420 | 19 (0)| 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("P"."ID"=880) 4 - filter("F"."PID"=880) Note ----- - dynamic sampling used for this statement (level=2) Statistics ---------------------------------------------------------- 5 recursive calls 0 db block gets 137 consistent gets 0 physical reads 0 redo size 1094 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 14 rows processed从执行计划中,我们可以看出,对f表进行了全表扫描,试想一下,如果p表不是返回1条记录,而是返回多条记录,那f表是不是要做多次的全部扫描?答案是肯定的,为了避免对f表的全表扫描,我们应该在外键上创建索引,如下所示:
SQL> create index fk on f(pid); Index created. SQL> set autotrace traceonly SQL> set line 1000 SQL> select p.id,p.name,f.name from p,f where p.id=f.pid and p.id=880; 14 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2077701003 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | 840 | 16 (0)| 00:00:01 | | 1 | NESTED LOOPS | | 14 | 840 | 16 (0)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| P | 1 | 30 | 0 (0)| 00:00:01 | |* 3 | INDEX UNIQUE SCAN | PK | 1 | | 0 (0)| 00:00:01 | | 4 | TABLE ACCESS BY IN