[Oracle] About Primary and foreign key design

Source: Internet
Author: User

The primary and Foreign keys have two major features:

1) The primary key itself is a unique index, ensuring the uniqueness of the column where the primary key is located;

2) The foreign key column must have corresponding records in the primary key column of the master table.

You must create an index on the foreign key.

We know that the primary key itself is a unique index, and the foreign key is a constraint. By default, there is no index, but we strongly recommend that you create an index on the foreign key in actual use. The following are two examples:

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.
The preceding statement creates two tables, where Table p has a primary key and table f has a foreign key. However, we have not created an index on the foreign key. Let's take a look at the execution plan and performance associated with the two tables:
SQL> set autotrace traceonlySQL> set line 1000SQL> 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
From the execution plan, we can see that a full table scan is performed on table f. Imagine if Table p does not return one record, but multiple records, does table f need to perform multiple scans? The answer is yes. To avoid full table scan for table f, we should create an index on the foreign key, as shown below:
SQL> create index fk on f(pid);Index created.SQL> set autotrace traceonlySQL> set line 1000SQL> 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 INDEX ROWID| F    |    14 |   420 |    16   (0)| 00:00:01 ||*  5 |    INDEX RANGE SCAN          | FK   |    14 |       |     1   (0)| 00:00:01 |-------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   3 - access("P"."ID"=880)   5 - access("F"."PID"=880)Note-----   - dynamic sampling used for this statement (level=2)Statistics----------------------------------------------------------          9  recursive calls          0  db block gets         90  consistent gets          1  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
From the preceding execution plan, we can know that the f table is indexed instead of a full table scan.

In addition to the above reasons, another advantage of foreign key index building is to avoid lock contention. Let's look at the example below:

First, we delete the index on the foreign key, and then delete a record on table f without submitting the record.

SQL> drop index fk;Index dropped.SQL> delete from f where id=2;1 row deleted.
Next, we opened another session and randomly deleted a piece of data from the p table. We found that it was blocked:
SQL> delete from p where id=2000;
The following are the locks:
SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by sid, type;       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK---------- -- ---------- ---------- ---------- ---------- ----------       254 TM      14356          0          3          0          0       254 TM      14359          0          3          0          1       254 TX     655369       1307          6          0          0      1388 TM      14359          0          0          4          0      1388 TM      14356          0          3          0          0
Here we are surprised to find that the table where the primary key p is located, because the table f where the foreign key is located deletes a record at will, the table p is completely locked, no DML operations can be performed. How terrible is this! Easy way to change to a primary key

If the production system has a field in a large table that meets the primary key condition and has no duplicate records, but is only a common index, how can we change it to a primary key?

Because the primary key creation operation is actually to create a unique index and add another constraint, we only need to add one constraint:

SQL> create index normal_idx on p(id);Index created.SQL> alter table p add constraint pk primary key(id);             Table altered.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.