2017-06-22 21 views
0

私は1つのアドレステーブル(k_id-PK、アドレスを含む)と1つのadd_histログテーブル(k_id、アドレス、変更日付を含む)を持っています。条件付きでテーブルを更新する方法

アドレステーブルのアドレス列を更新する更新クエリを作成したいので、add_histテーブルから最新のアドレスをフェッチするとジョブが実行されます。私のクエリはほとんど完了しています。正しい結果が得られます。しかし、私はアドレステーブルが既に更新されている場合は、それを更新しないでください。ここで私のquery.Pleaseを見直し、希望の結果を得るために修正してください。

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 - あなたの将来のためにオラクルのオフィシャルトレーニングセッション: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   

同じ最終結果ですが、更新された2行ではなく1行がマージされました。

where句を使用せずにマージを実行すると、4つの行はすべて影響を受けますが、nullチェックなしでID 2の行のみが更新されます)。

+0

あまりにも多くの問題を抱えているAlexさん、本当にありがとうございました...私は固執しました...その本当に大きな助け:) –

+1

まったく同じ結果ほとんど同じ構文)を 'update 'で実現することができます。 'where'節も' null 'を扱うべきであることに注意してください( 'not null'制約が" address "列に存在しないことはよくありません)。 – mathguy

1

UPDATEステートメントで目的の結果を得ることができます。具体的には、「参加によって更新する」必要があります。しかし構文は正確でなければなりません。 Update with joins

アレックスの答えと同じ設定を使用すると、次のupdateステートメントは1つの行を更新します。

EDIT:この回答の下のAlex Pooleのコメントを参照してください。ここで提案されているソリューションは、Oracle 12.1以上でのみ動作します。この問題は、「結合による更新」の概念ではなく、集約の結果であるソース行セットです。これは、コンパイル時に、ソース行セットの「結合」列が一意である(重複がない)ことをOracleが認識している方法と関係しています。古いバージョンのOracleでは、明示的な一意キー制約または主キー制約または索引が必要でした。もちろん、GROUP BY <col>の場合、<col>は集計の結果セットで一意になりますが、ユニークな制約やインデックスはありません。オラクルはこの状況を認識しているようですが、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

ありがとうございます@mathguy ...その結果を見つけるさまざまな方法を知って本当にいいです –

+0

これは12cR1(おそらくR2は、しかし、チェックすることはできません)が、まだ私のダミーテーブルで11gR2のORA-01779を取得します。 10gR2では 'bypass_ujvc'ヒント(文書化されていません!)を追加して修正しましたが、それは11gR2ではこれ以上動作しません。 OPは独自のテーブルで異なる結果を得たり、もちろん12cにあるかもしれません。 –

+0

@AlexPoole - 面白い! 'merge'と' update through join'の主な違いは、ソーステーブルの結合カラムの一意性です。 'update'は、コンパイル時に"既知 "であることが必要です。私は実際には試していませんでしたが、ドキュメントの記事で書いた例は、古いバージョンのOracleで動作しています(結合カラムはソーステーブルの明示的にPKと宣言されています)。このAnswerを投稿する前に、ソーステーブルが集約サブクエリの結果であっても機能するかどうかはわかりませんでしたが、それは行いました。 (続き) – mathguy

関連する問題