2017-06-22 52 views
0

我有一個地址表(包含k_id-PK,地址)和一個add_hist日誌表(包含k_id,地址,更改日期),即它具有每個id的所有地址以及哪個日期地址更改。如何有條件地更新表

我想做一個更新查詢,它將更新地址表中的地址列,以便從add_hist表中獲取最新地址將完成作業。我幾乎完成了我的查詢。它也取得了正確的結果。但我想如果地址表已經更新,然後不更新它。這裏去我的查詢。請檢查並更正它以獲得所需的結果。

update address a set k_add = 
(select kad from (
select h.k_id kid, h.k_add kad, h.chg_dt from add_hist h, 
(select k_id, max(chg_dt) ch from add_hist 
group by k_id 
) h1 
where h1.k_id = h.k_id 
and h1.ch=h.chg_dt 
) h2 
where h2.kid = a.k_id) 
; 
+0

我沒有看到一種方式直接讓你在單個更新語句中進行檢查。您必須首先將kad輸出(來自子查詢)保留在變量中,然後只有在該值與表中的值不同時才執行更新。 –

+0

謝謝你Renato :) –

+1

@RenatoAfonso - 爲您的將來Oracle培訓課程:https:// stackoverflow。com/documentation/oracle/8061/update-with-joins#t = 201706230023201170024 – mathguy

回答

1

你可以使用一個合併而非更新:

merge into address a 
using (
    select k_id, max(k_add) keep (dense_rank last order by chg_dt) as k_add 
    from add_hist 
    group by k_id 
) h 
on (a.k_id = h.k_id) 
when matched then 
    update set a.k_add = h.k_add 
    where (a.k_add is null and h.k_add is not null) 
    or (a.k_add is not null and h.k_add is null) 
    or a.k_add != h.k_add; 

using子句中的查詢查找最近的地址從歷史表中的每個ID。當更新的主表上存在匹配的ID時 - 但只有當值不同時,由於where子句。

隨着一些虛擬的數據:

create table address (k_id number primary key, k_add varchar2(20)); 
create table add_hist (k_id number, k_add varchar2(20), chg_dt date); 

insert into address (k_id, k_add) values (1, 'Address 1'); 
insert into address (k_id, k_add) values (2, 'Address 2'); 
insert into address (k_id, k_add) values (3, null); 
insert into address (k_id, k_add) values (4, null); 

insert into add_hist (k_id, k_add, chg_dt) values (1, 'Address 1', date '2017-01-01'); 
insert into add_hist (k_id, k_add, chg_dt) values (1, 'Address 2', date '2017-01-02'); 
insert into add_hist (k_id, k_add, chg_dt) values (1, 'Address 1', date '2017-01-03'); 

insert into add_hist (k_id, k_add, chg_dt) values (2, 'Address 1', date '2017-01-01'); 
insert into add_hist (k_id, k_add, chg_dt) values (2, 'Address 2', date '2017-01-02'); 
insert into add_hist (k_id, k_add, chg_dt) values (2, 'Address 3', date '2017-01-03'); 

insert into add_hist (k_id, k_add, chg_dt) values (3, 'Address 1', date '2017-01-01'); 
insert into add_hist (k_id, k_add, chg_dt) values (3, null, date '2017-01-02'); 

insert into add_hist (k_id, k_add, chg_dt) values (4, 'Address 1', date '2017-01-01'); 

commit; 

運行您的更新語句獲取:

4 rows updated. 

select * from address; 

     K_ID K_ADD    
---------- -------------------- 
     1 Address 1   
     2 Address 3   
     3      
     4 Address 1   

回滾到起始狀態後,運行合併得到:

2 rows merged. 

select * from address; 

     K_ID K_ADD    
---------- -------------------- 
     1 Address 1   
     2 Address 3   
     3      
     4 Address 1   

同最終結果,但1行合併而不是2行更新。 (如果在沒有where子句的情況下運行合併,則所有四行仍然受到影響;如果沒有空檢查,則只更新ID爲2的行)。

+0

非常感謝亞歷克斯帶來了如此多的麻煩......我被困住了......它的確幫助很大:) –

+1

完全一樣的結果和幾乎相同的語法)可以通過'update'來實現。請注意where子句也應該處理null(除非在「address」列上存在'not null'約束,這通常不是這種情況)。 – mathguy

1

您可以使用UPDATE語句來達到預期結果。具體而言,您需要「通過連接進行更新」。但語法必須精確。 Update with joins

使用與Alex的回答相同的設置,以下update語句將更新一行。

編輯:請參閱下面的答覆亞歷克斯普爾的評論。此處提出的解決方案僅適用於Oracle 12.1及更高版本。問題不在於「通過連接更新」概念,而在於源聚合行是聚合的結果。它與Oracle在編譯時知道源行集中的「連接」列是唯一的(它沒有重複)有關。在舊版本的Oracle中,需要顯式唯一或主鍵約束或索引。當然,當我們GROUP BY <col>時,<col>在聚集的結果集中將是唯一的,但它不會有唯一的約束或索引。看起來Oracle認識到了這種情況,並且從12.1開始,它允許update through join,其中源表是聚合的結果,如本例所示。

update 
    (select a.k_add as current_address, q.new_address 
    from (
      select k_id, 
         min(k_add) keep (dense_rank last order by chg_dt) as new_address 
      from  add_hist 
      group by k_id 
      )  q 
      join 
      address a on a.k_id = q.k_id 
) 
set current_address = new_address 
where current_address != new_address 
    or current_address is null and new_address is not null 
    or current_address is not null and new_address is null 
; 
+0

Thanks @mathguy ...它真的很高興知道不同的查找結果的方式 –

+0

這適用於12cR1(大概是R2,但無法檢查),但仍然在11gR2中獲得ORA-01779與我的虛擬表。在10gR2中添加'bypass_ujvc'提示(未記錄!)'修復'它,但在11gR2中似乎不再有效。 OP可能會得到不同的結果與自己的表,和/或可能在12c,當然... –

+0

@AlexPoole - 有趣! 'merge'和'update through join'的主要區別在於源表中連接列的唯一性:'update'要求在編譯時「知道」,而'merge'只在運行時檢查。我沒有真正嘗試,但我幾乎可以肯定,我在「文檔」文章中編寫的例子在較早版本的Oracle中工作(連接列在源表中顯式聲明爲PK)。我不知道,在我發佈這個答案之前,如果它在源表是聚合子查詢的結果時工作,但它確實如此。 (續) – mathguy