( "A" VARCHAR2(20 BYTE),
"B" VARCHAR2(10 BYTE),
"DETPNO" VARCHAR2(10 BYTE)
)TABLESPACE "USERS" ;
CREATE TABLE "SCOTT"."MID_B_TAB"
( "A" VARCHAR2(20 BYTE),
"B" VARCHAR2(10 BYTE),
"DEPTNO" VARCHAR2(10 BYTE)
)TABLESPACE "USERS" ;
--給MID_A_TAB表添加主鍵
alter table mid_a_tab add constraint a_pk primary key (detpno);
--給MID_B_TAB表添加主鍵
alter table mid_b_tab add constraint b_pk primary key(a);
--給子表MID_B_TAB添加外鍵,並且引用主表MID_A_TAB的DETPNO列,並通過on delete cascade指定引用行爲是級聯刪除
alter table mid_b_tab add constraint b_fk foreign key (deptno) references mid_a_tab (detpno) on delete cascade;
--向這樣就創建了好子表和主表
向主表添加數據記錄
SQL> insert into mid_a_tab(a,b,detpno) values('1','1','10');
已創建 1 行。
已用時間: 00: 00: 00.00
向子表添加數據
SQL> insert into mid_b_tab(a,b,deptno) values('1','2','6');
insert into mid_b_tab values('1','2','6')
*
第 1 行出現錯誤:
ORA-00001: 違反唯一約束條件 (SCOTT.B_PK)
已用時間: 00: 00: 00.00
可見上面的異常信息,那時因爲子表插入的deptno的值是6,然而此時我們主表中
detpno列只有一條記錄那就是10,所以當子表插入數據時,在父表中不能夠找到該引用
列的記錄,所以出現異常。
但我們可以這樣對子表的數據的進行插入(即:在子表的deptno列插入null,因爲我們在建表的時候
並沒有對該列進行not null的約束限制):
SQL> insert into mid_b_tab(a,b,deptno) values('3','2',null);
已創建 1 行。
已用時間: 00: 00: 00.00
現在如果我們把子表mid_b_tab中deptno列加上not null約束。
SQL> alter table mid_b_tab modify deptno not null;
alter table mid_b_tab modify deptno not null
*
第 1 行出現錯誤:
ORA-02296: 無法啓用 (SCOTT.) - 找到空值
已用時間: 00: 00: 00.01
上面又出現異常,這是因爲現在mid_b_tab表中有了一條記錄,就是我們先前添加的
那條記錄。
3,2,null
現在我們要把該表的deptno列進行not null約束限制,所以oracle不讓我們這樣幹。
那我們就只有把該表給delete或truncate掉,然後在修改deptno列爲非空。
SQL> delete from mid_b_tab;
已刪除2行。
已用時間: 00: 00: 00.01
再次修改子表mid_b_tab表的deptno列爲非空。
SQL> alter table mid_b_tab modify deptno not null;
表已更改。
已用時間: 00: 00: 00.01
修改成功!
我們再次插入數據
insert into mid_b_tab(a,b,deptno) values('13','2',null);試試。
SQL> insert into mid_b_tab(a,b,deptno) values('13','2',null);
insert into mid_b_tab(a,b,deptno) values('13','2',null)
*
第 1 行出現錯誤:
ORA-01400: 無法將 NULL 插入 ("SCOTT"."MID_B_TAB"."DEPTNO")
已用時間: 00: 00: 00.00
看見現在oracle不讓我們插入空值了。
所以我們在創建子表的外鍵約束時,該表的引用列必須要進行not null限制,也可以在
該列創建unique,或primary key約束,並且引用列與被引用列的數據類型必須相同。
SQL> insert into mid_b_tab(a,b,deptno) values('13','2','10');
已創建 1 行。
已用時間: 00: 00: 00.01
此時數據插入成功,因爲此時插入的10,在主表中的被引用列中已經存在了。
現在我們一系列的操作:
SQL> select * from mid_b_tab ;
A B DE
-------------------- ---------- --
13 2 10
已用時間: 00: 00: 00.00
SQL> select * from mid_a_tab;
A B DE
-------------------- ---------- --
1 1 10
已用時間: 00: 00: 00.00
SQL> delete from mid_a_tab;
已刪除 1 行。
已用時間: 00: 00: 00.01
SQL> select * from mid_b_tab;
未選定行
從上邊的操作中可以看出當我們刪除了主表中的記錄後,子表中相應的記錄
也被級聯刪除掉了。
通過引用行爲可以確定如何處理子表中的外鍵字段。引用類型包括3中類型:
1.on delete cascade;--級聯刪除
2.on set null;--刪除主表中的記錄後,子表中的相應記錄的列被設置爲null(但子表的該字段必須支持null值)。
3.on no action;--不允許刪除主表中被引用的數據,該操作會被禁止。
如果有on delete cascade,而且沒有在子表上加索引:那麼每刪除主表中的一行
都會對子表做一個全表掃描。而且如果從父表刪除多行,那麼每從父表中刪除一行
就要掃描一次子表。
SQL> select * from mid_a_tab a,mid_b_tab b where
2 a.detpno=b.deptno;
A B DETPNO A B DEPTNO
-------------------- ---------- ---------- -------------------- ---------- ----------
1 2 12 2 2 12
1 2 12 1 1 12
2 22 13 22 212 13
3 33 14 55 6666 14
已用時間: 00: 00: 00.00
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=4 Bytes=208
)
1 0 HASH JOIN (Cost=7 Card=4 Bytes=208)
2 1 TABLE ACCESS (FULL) OF 'MID_A_TAB' (TABLE) (Cost=3 Card=
3 Bytes=78)
3 1 TABLE ACCESS (FULL) OF 'MID_B_TAB' (TABLE) (Cost=3 Card=
4 Bytes=104)
統計信息
----------------------------------------------------------
210 recursive calls
0 db block gets
74 consistent gets
0 physical reads
0 redo size
748 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
8 sorts (memory)
0 sorts (disk)
4 rows processed
SQL> create index mid_b_index on mid_b_tab(deptno);
索引已創建。
已用時間: 00: 00: 00.00
SQL> select * from mid_a_tab a,mid_b_tab b where
2 a.detpno=b.deptno;
A B DETPNO A B DE
-------------------- ---------- ---------- -------------------- ---------- --
1 2 12 2 2 12
1 2 12 1 1 12
2 22 13 22 212 13
3 33 14 55 6666 14
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=6 Card=4 Bytes=208
)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MID_B_TAB' (TABLE) (Cost
=1 Card=1 Bytes=26)
2 1 NESTED LOOPS (Cost=6 Card=4 Bytes=208)
3 2 TABLE ACCESS (FULL) OF 'MID_A_TAB' (TABLE) (Cost=3 Car
d=3 Bytes=78)
4 2 INDEX (RANGE SCAN) OF 'MID_B_INDEX' (INDEX) (Cost=0 Ca
rd=4)
統計信息
----------------------------------------------------------
10 recursive calls --訪問數據字典得到元數據。第二次執行相同語句,遞歸調用基本爲零
0 db block gets -- 指DML語句所得到的數據塊個數
31 consistent gets --重要!!指select語句所得到的數據塊個數
0 physical reads --硬盤上讀出的數據
0 redo size --產生的日誌
748 bytes sent via SQL*Net to client --網絡流量指標
512 bytes received via SQL*Net from client --網絡流量指標
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
4 rows processed
大家可以從上面的執行計劃中看出,向子表添加索引前後查詢的差別。如果兩表中的數據量再大點的話那麼效果可能會更明顯。