1. 程式人生 > >Oracle 兩表關聯更新

Oracle 兩表關聯更新

 有TA, TB兩表,假設均有三個欄位id, name, remark. 現在需要把TB表的name, remark兩個欄位通過id關聯,更新到TA表的對應欄位。

建表指令碼:

  1. droptable TA;  
  2. createtable TA  
  3. (  
  4. id number notnull,  
  5. namevarchar(10) notnull,  
  6. remark varchar(10) notnull
  7. );  
  8. droptable TB;  
  9. createtable TB  
  10. (  
  11. id number notnull,  
  12. namevarchar(10) notnull,  
  13. remark varchar
    (10) notnull
  14. );  
  15. truncatetable TA;  
  16. insertinto TA values(1, 'Aname1''Aremak1');  
  17. insertinto TA values(2, 'Aname2''Aremak2');  
  18. commit;  
  19. truncatetable TB;  
  20. insertinto TB values(1, 'Bname1''Bremak1');  
  21. insertinto TB values(3, 'Bname3''Bremak3');  
  22. commit;  
  23. select * from TA;  
  24. select * from
     TB;  

SQLServer/Oracle版本的Update寫法分別如下:

1. SQLServer

  1. update TA setname=b.name, remark=b.remark from TA a innerjoin TB b on a.id = b.id  

或者

  1. update TA setname=b.name, remark=b.remark from TA a, TB b where a.id = b.id  

注意不要在被更新表的的欄位前面加別名字首,否則語法靜態檢查沒問題,實際執行會報錯。

Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "a.name" could not be bound.

2. Oracle

  1. update TA a set(name, remark)=(select b.name, b.remark from TB b where b.id=a.id)   
  2. where exists(select 1 from TB b where b.id=a.id)  

注意如果不新增後面的exists語句,TA關聯不到的行name, remark欄位將被更新為NULL值, 如果name, remark欄位不允許為null,則報錯。 這不是我們希望看到的。

  1. --when name, remark is not null, cause error. 
  2. --if allow null, rows in TA not matched will be update to null.
  3. update TA a set(name, remark)=(select b.name, b.remark from TB b where b.id=a.id);  

可考慮的替代方法:

  1. update TA a setname= nvl((select b.namefrom TB b where b.id=a.id), a.name);  
  2. update TA a set remark= nvl((select b.remark from TB b where b.id=a.id), a.remark);  

如果TA.id, TB.id是unique index或primary key

可以使用檢視更新的語法:

  1. ALTERTABLE TA ADDCONSTRAINT TA_PK  
  2.   PRIMARYKEY (  
  3.   ID  
  4. )  
  5.  ENABLE  
  6.  VALIDATE  
  7. ;  
  8. ALTERTABLE TB ADDCONSTRAINT TB_PK  
  9.   PRIMARYKEY (  
  10.   ID  
  11. )  
  12.  ENABLE  
  13.  VALIDATE  
  14. ;  
  15. update (select a.name, b.nameas newname,   
  16. a.remark, b.remark as newremark from TA a, TB b where a.id=b.id)  
  17. setname=newname, remark=newremark;                                                           
  18. -------------------------------------------------------------
  19. Oracle有下面兩個表:將表tab1中id值與和表tab2中id值相同的行的val更新為tab2中val的值.
    select * from tab1;


    select * from tab2

    最容易犯的錯誤是:update tab1 set val=(select val from tab2 where tab1.id=tab2.id);
    更新完後的結果是:select * from tab1,在tab1中有的行,如果在tab2中沒有對應的行,值被更新為null

    改正為:update tab1 set val=(select val from tab2 where tab1.id=tab2.id)
    where exists (select 1 from tab2 where tab1.id=tab2.id)
    但是如果tab2中有多條對應tab1中一條的情況也會出錯.
    最好的方法是用merge語法:
    1. merge into tab1  
    2. using tab2  
    3. on(tab1.id=tab2.id)  
    4. when matched then  
    5. update set tab1.val = tab2.val  
    同樣,如果tab2中有多條對應tab1中一條的情況也會出錯:ORA-30926: unable to get a stable set of rows in the source tables
    比如在tab2中再插入一條 insert into tab2 values(2,'xxxx')
    可以通過在using中的subquery中將重複記錄過濾來避免這種錯誤,merge終極版:
    1. merge into tab1  
    2. using  (select * FROM tab2 X  WHERE  X.ROWID =  
    3. (SELECT MAX(Y.ROWID) FROM  tab2 Y  WHERE  X.ID = Y.ID)) tab2  
    4. on(tab1.id=tab2.id)  
    5. when matched then  
    6. update set tab1.val = tab2.val