如何有条件地更新表

时间:2017-06-22 11:19:18

标签: database oracle plsql

我有一个地址表(包含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)
;

2 个答案:

答案 0 :(得分: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的行。

答案 1 :(得分:1)

您可以使用UPDATE语句获得所需的结果。具体来说,您需要通过加入进行更新。"但语法必须精确。 Update with joins

使用与Alex的答案相同的设置,以下update语句将更新一行。

编辑:请参阅此答案下方的Alex Poole的评论。此处提出的解决方案仅适用于Oracle 12.1及更高版本。问题不在于通过加入&#34;更新。概念,但源行集是聚合的结果。它与Oracle在编译时知道&#34; join&#34;的关系有关。源行集中的列是唯一的(它没有重复项)。在旧版本的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
;