foreign key constraints
DESCRIPTION
At this point, we now start loading into EMP, and it acquires some extra extents to accommodate the data, too: Fragmentation of a tablespace occurs when lots of pockets of free space are available within a tablespace, but none of them are of a sufficient size to house new extents for segments.TRANSCRIPT
Foreign Key Constraints Administration Tips
Copyright © Howard Rogers 2001 10/18/2001 Page 1 of 3
Foreign Key Constraints Foreign key constraints are used to enforce referential integrity between tables. That is, you create a constraint on Table A, Column 5 declaring that it shall not contain values which have not previously been committed in Table B, Column 1. Table B in this example, containing the previously-validated data, is known as the "parent" table of the relationship. Table A, into which new records are being inserted which must reference the parent table, is known as the "child" table. To set up such a parent-child relationship at table creation time, you would issue the following sequence of commands: First the parent: CREATE TABLE CUSTOMERS ( CUSTID NUMBER CONSTRAINT CUST_CUSTID_PK PRIMARY KEY, CUSTNAME CHAR(20)); Now the child: CREATE TABLE ORDERS ( ORDERID NUMBER CONSTRAINT ORDERS_ORDERID_PK PRIMARY KEY, CUSTNUM NUMBER CONSTRAINT CUSTNUM_FK REFERENCES CUSTOMERS(CUSTID), ORDERVAL NUMBER(8,2)); A couple of things to notice here. Firstly, all constraints are named. Get into the habit of doing that, otherwise you have to live with the awful system-generated names that get assigned when constraint names are omitted. They're obscure and difficult to work with! Secondly, note that the column being referenced by the child is the Primary Key of the parent table. That is not always required -but at the very least, the column being referenced must have a unique constraint declared against it (in the world of Oracle, at least, a child cannot have two parents!). Primary keys are more usual, though. Thirdly, notice that the referenced column in the parent table is called 'custid', but in the child table, the equivalent column is called 'custnum'. The names of the related columns are not important, and can differ as much as you like. But the data types must agree. That is, if custid had been a char(15), then custnum would also have had to have been declared as a char datatype. If one is a number, the other must be too. If the two data types don't agree, you'll get an error: ORA-02267: COLUMN TYPE INCOMPATIBLE WITH REFERENCED COLUMN TYPE. However, the declared length of the fields doesn't necessarily have to tie up precisely (though you'll have problems later if they don't). A "number (5)" can quite happily reference a "number(6)" in the parent table.
Foreign Key Constraints Administration Tips
Copyright © Howard Rogers 2001 10/18/2001 Page 2 of 3
You may have used products such as Access, which allow updates to the parent table's data to be 'cascaded' down to the child. If, for example, we update a customer's number, we'd like all records in the child table that refer to that customer to pick up the new number. That can not be done in Oracle, at least not using the basic foreign key constraint definitions. You'd have to write a trigger instead. You can, however, specify that if a parent record is deleted then all child records should be deleted too (the default behaviour is that deletes of a parent record is simply not permitted if it would leave orphan child records behind). To do that, you issue this sort of command for the creation of the child table: CREATE TABLE ORDERS ( ORDERID NUMBER CONSTRAINT ORDERS_ORDERID_PK PRIMARY KEY, CUSTNUM CHAR(5) CONSTRAINT CUSTNUM_FK REFERENCES CUSTOMERS(CUSTID) ON DELETE CASCADE, ORDERVAL NUMBER(8,2)); Note the new key words “on delete cascade” there. Once this sort of cascading delete has been specified, we might see the following consequences: SQL> SELECT * FROM ORDERS; ORDERID CUSTID ORDERVAL ---------- ------ ---------- 1 789 350 2 789 7500 3 123 7500 SQL> SELECT * FROM CUSTOMERS; CUSTI CUSTNAME ----- -------------------- 789 SMITH 123 ROGERS SQL> DELETE FROM CUSTOMERS WHERE CUSTID='789'; 1 ROW DELETED. SQL> COMMIT; COMMIT COMPLETE. SQL> SELECT * FROM ORDERS;
Foreign Key Constraints Administration Tips
Copyright © Howard Rogers 2001 10/18/2001 Page 3 of 3
ORDERID CUSTID ORDERVAL ---------- ------ ---------- 3 123 7500 Notice how the delete of a single customer record resulted in the delete of 2 order records -but the SQL Plus confirmation of the delete somewhat (misleadingly) neglects to mention the child record deletions! Apart from these considerations, there is one extremely nasty aspect to foreign key constraints which it would seem that hardly any developer of my acquaintance is aware of (though they should be -it's in the Oracle documentation!). It is simply this: if I perform DML on any part of the PARENT table, the ENTIRE child table is locked out from all DML for the duration of the transaction. Selects can be done against the child table, because the lock involved is a share lock. But no DML whatsoever is permitted. That is an extremely expensive transaction! Fortunately, there's a fix for this: create an index on the column in the child table which is referencing the parent. If, pursuing the example we've seen earlier, I was to issue the command: CREATE INDEX ORDER_CUST_IDX ON ORDERS(CUSTNUM); ...which is an index on the child table, then no expensive locking of the entire child table takes place when I perform DML on the parent table. This business of child table locking is so nasty and surprising that it leads to quite a simple rule of thumb: whenever you declare a foreign key constraint on a child table, slap an index on that column afterwards. You may never use that index. It may be taking up unwelcome amounts of space, and its dead-weight presence will be slowing down your regular DML. But how much more unwelcome would it be not to be allowed to insert new orders because one customer wants to have his contact details amended? Incidentally, this issue is resolved in Oracle 9i: if there is no index on the child table's foreign key, then the table lock is taken as the DML on the parent table is started -but it is then immediately released. There is therefore no need to slap unnecessary indexes on child tables in 9i.