UPDATE关联表
http://www.itpub.net/thread-810466-1-1.html
我最近一直想研究一下UPDATE語句,尤其是多表關聯UPDATE的時候,很容易出問題,于是我就在PUB上問,在資料上查,現在我終于弄明白了。
對我幫助的帖子來源于http://www.itpub.net/showthread. ... 10&pagenumber=1
我把實驗的思路整理如下,希望對不知道的人有幫助!
我的結論是這樣的
更新表的方式有三種方法
1、
其中最普通的是update t1 set b=(select b from t2 where t1.a=t2.a);
但是,要注意空值的影響,
如果怕空值的影響,要寫成
update t1 set b= (select b from t2 where t1.a=t2.a)
where exists
(select 1 from t2 where t1.a=t2.a);
2、
update (
select /*+use_hash(t1,t2)*/ t1.b b1,t2.b b2
from t1,t2 where t1.a=t2.a)
set b1=b2;
這種方法效率高,但是要注意兩個關聯字段都要有唯一性索引!
3、存儲過程
SQL> declare
2 cursor c is
3 select t1.*,t1.rowid from t1;
4 begin
5 for c1 in c loop
6 update t1 set b=
7 (select b from t2 where a=c1.a)
8 where rowid=c1.rowid
9 and
10 exists
11 (select 1 from t2 where c1.a=t2.a);
12 end loop;
13 end;
14 /
但是還是要注意要有exists的語句,否則一樣解決不了空值問題
下面實驗如下:
SQL> select * from t1;
A B
---------- ----------
1 1
2 2
3 4
4 4
1
2
4
已選擇7行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL> update t1 set b=(select b from t2 where t1.a=t2.a);
已更新7行。
SQL> select * from t1;
A B
---------- ----------
1 2
2 5
3 7
4
1 2
2 5
4
已選擇7行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL>
現在ROLLBACK還原,還是原來表的記錄如下,加EXISTS操作看看有什么變化
SQL> select * from t1;
A B
---------- ----------
1 1
2 2
3 4
4 4
1
2
4
已選擇7行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL> update t1 set b= (select b from t2 where t1.a=t2.a)
2 where exists
3 (select 1 from t2 where t1.a=t2.a);
已更新5行。
SQL> select * from t1;
A B
---------- ----------
1 2
2 5
3 7
4 4
1 2
2 5
4
已選擇7行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL>
謝謝!
現在我實驗明白了,如果不加
where exists
(select 1 from t2 where t1.a=t2.a);
t1表的a,b字段有4,4的一條記錄,由于在t2表中a,b字段不存在a字段值為4的記錄.這樣在UPDATE的時候,在t2表中找不到就會用null去UPDATE t1表的4,4為4,null,這可不是我們愿意看到的.
但加了那個EXISTS,問題就避免了。
UPDATE關聯表容易出現的第二個錯誤,也就是著名的
ORA-01427: 單行子查詢返回多個行
在這里也能得到實驗
回滾原來那兩張表
SQL> rollback;
回退已完成。
SQL> select * from t1;
A B
---------- ----------
1 1
2 2
3 4
4 4
1
2
4
已選擇7行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL> update t2 set b= (select b from t1 where t2.a=t1.a);
update t2 set b= (select b from t1 where t2.a=t1.a)
*
第 1 行出現錯誤:
ORA-01427: 單行子查詢返回多個行
SQL>
注意到,如果兩張表完全一致,t1.a完全和t2.a一一對應,都是唯一的,那就不可能出現這樣錯誤
如果兩邊不一致,比如t1的a值多余t2的a值或者反過來,我們就要特別注意了。
講白點就是,多的表允許用少的表來更新,多的表的反正都被少的那個表更新(說準確點應該是少的表都要是唯一的a記錄)
少的表,不允許被多的表來更新!
再舉個例子
如果兩邊都有重復,那別管是利用t1更新t2還是利用t2更新t1都別想成功了
SQL> insert into t1 values (3,5);
已創建 1 行。
SQL> insert into t2 values (3,8);
已創建 1 行。
SQL> select * from t1;
A B
---------- ----------
3 5
1 1
2 2
3 4
4 4
1
2
4
已選擇8行。
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
3 8
SQL> update t1 set b=(select b from t2 where t1.a=t2.a);
update t1 set b=(select b from t2 where t1.a=t2.a)
*
第 1 行出現錯誤:
ORA-01427: 單行子查詢返回多個行
SQL> update t2 set b=(select b from t1 where t2.a=t1.a);
update t2 set b=(select b from t1 where t2.a=t1.a)
*
第 1 行出現錯誤:
ORA-01427: 單行子查詢返回多個行
SQL>
做完這些后,聽說有另外一種方法能更高效的更新,于是再做實驗
就是如下方法了,但是報錯了
SQL> update (
2 select /*+use_hash(t1,t2)*/ t1.b b1,t2.b b2
3 from t1,t2
4 where t1.a=t2.a)
5 set b1=b2;
set b1=b2
*
第 5 行出現錯誤:
ORA-01779: 無法修改與非鍵值保存表對應的列
聽說這種方法要保證兩表都是唯一值才可以,只好刪除掉重復記錄
SQL> delete t1 where rowid in (select rid from(select rowid rid,row_number() ove
r(partition by a order by a desc) rn from t1 )where rn > 1) ;
已刪除3行。
SQL> select * from t1;
A B
---------- ----------
1 1
3 4
2
4
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
接著再更新,哇,又報錯
SQL> update (
2 select /*+use_hash(t1,t2)*/ t1.b b1,t2.b b2
3 from t1,t2
4 where t1.a=t1.b)
5 set b1=b2;
set b1=b2
*
第 5 行出現錯誤:
ORA-01779: 無法修改與非鍵值保存表對應的列
是不是要建唯一性索引才可以呢?
SQL> create unique index indx_t1_a on t1(a);
索引已創建。
SQL> create unique index indx_t2_a on t2(a);
索引已創建。
再看看,這下可以了,看來網絡上說的這種方法高效,但是也挺苛刻的,還要保證唯一性索引,兩張表都要保證
SQL> update (
2 select /*+use_hash(t1,t2) */t1.b b1,t2.b b2
3 from t1,t2
4 where t1.a=t2.a)
5 set b1=b2;
已更新3行。
SQL>
SQL> select * from t1;
A B
---------- ----------
1 2
3 7
2 5
4
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
以上做了這么多實驗,收獲不小,也謝謝PUB上的兄弟的幫忙,不過后來想存儲過程實現是不是也實驗一把呢,算是對這個多表更新的一種全面的總結了。
再實驗吧
SQL> rollback;
回退已完成。
SQL> select * from t1;
A B
---------- ----------
1 1
3 4
2
4
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL> declare
2 cursor c is
3 select t1.*,t1.rowid from t1;
4 begin
5 for c1 in c loop
6 update t1 set b=
7 (select b from t2 where a=c1.a)
8 where rowid=c1.rowid;
9 end loop;
10 end;
11 /
PL/SQL 過程已成功完成。
SQL> select * from t1;
A B
---------- ----------
1 2
3 7
2 5
4
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL>
這個更新應該和普通的第一種更新要考慮的東西是一樣的吧,會不會有空值的因素在里面呢?
測試,果然,這個存儲過程更新的方法也要考慮空值才可以,否則也會不對,入下
SQL> select * from t1;
A B
---------- ----------
1 2
3 7
2 5
4
SQL> select * from t2;
A B
---------- ----------
1 2
2 5
3 7
6
SQL> delete from t2 where a=1;
已刪除 1 行。
SQL> commit;
提交完成。
SQL> select * from t2;
A B
---------- ----------
2 5
3 7
6
SQL> declare
2 cursor c is
3 select t1.*,t1.rowid from t1;
4 begin
5 for c1 in c loop
6 update t1 set b=
7 (select b from t2 where a=c1.a)
8 where rowid=c1.rowid;
9 end loop;
10 end;
11 /
PL/SQL 過程已成功完成。
SQL> select * from t1;
A B
---------- ----------
1
3 7
2 5
4
SQL>
也是需要修改的,代碼如下
SQL> select * from t1;
A B
---------- ----------
1 2
3 7
2 5
4
SQL> select * from t2;
A B
---------- ----------
2 5
3 7
6
SQL> declare
2 cursor c is
3 select t1.*,t1.rowid from t1;
4 begin
5 for c1 in c loop
6 update t1 set b=
7 (select b from t2 where a=c1.a)
8 where rowid=c1.rowid
9 and
10 exists
11 (select 1 from t2 where c1.a=t2.a);
12 end loop;
13 end;
14 /
PL/SQL 過程已成功完成。
SQL> select * from t1;
A B
---------- ----------
1 2
3 7
2 5
4
SQL>
這下沒空值了
到這里為止,我的UPDATE多表關聯的研究終于告一段落了,收獲不小,希望看到我這個實驗的,原先也不的清楚的兄弟們能有收獲!
總結
- 上一篇: 主键约束、外键约束、唯一约束、检查约束、
- 下一篇: java call oracle pro