SQL Oracle .. 更新表的最新值

Posted

技术标签:

【中文标题】SQL Oracle .. 更新表的最新值【英文标题】:SQL Oracle.. Update table on latest value 【发布时间】:2019-11-26 14:54:40 【问题描述】:

我有一张包含姓名和地址的表格。地址有时会发生变化。 我需要将最后修改的地址更改为每个名称 表格是这样的

Name    Address
Samual  123 bixby
Samual  111 adelea
Arfan   12 spadina    
Arfan   22 Spadina
Atesao  Roman Alley
Atesao  Roman Alley
Arfan   12 spadina

输出应该是

Name    Address
Samual  111 adelea 
Samual  111 adelea
Arfan   12 spadina    
Arfan   12 Spadina
Atesao  Roman Alley
Atesao  Roman Alley
Arfan   12 spadina

我们需要用最新值更新全表

【问题讨论】:

首先我们需要一些东西来获取最新的值,有日期吗?任何身份证?或者其他的东西 ?表格中的数据未排序,因此如果您不向我们提供获取最新信息的方法,“最新”值将毫无意义。 看起来我们需要使用伪列来确定时间来了解上次更新/添加记录的时间。 oracle 文档中的详细信息 - docs.oracle.com/cd/B19306_01/server.102/b14200/… 如果表格上没有任何其他列,那么没有什么可以用来推断 "latest value" 你应该看看at this answer类似的问题。 【参考方案1】:

您可以使用merge,但考虑到您有一些列来标识最新记录。可以说它是creation_date。我也忽略了两个用户同名的场景。

Merge into your_table t
Using 
(select name, 
        Address,
        Row_number() over (partition by name order by creation_date desc) as rn
  From your_table) m
On (t.name = m.name and m.rn = 1)
When matched then set t.address = m.address;

干杯!!

【讨论】:

【参考方案2】:

假设您有一个 ID 列并确定该列的顺序,请考虑使用:

update tab t
   set t.address = (
                    with tt as
                    (
                     select row_number() over (partition by name order by id desc) as rn, 
                            t.*
                       from tab t
                    )
                    select tt.address
                      from tt 
                     where tt.name = t.name
                       and rn = 1
                   )

通过row_number() 解析函数中的partition by name 子句获取每个名称的最新地址。

Demo

【讨论】:

【参考方案3】:

没有行号等,假设您有一个 id 可以让我们检索最新值,您可以通过以下查询简单地实现您想要的:

UPDATE test t SET address = (
  SELECT address 
  FROM test t2 
  WHERE id = (
    SELECT MAX(id) 
    FROM test t3 
    WHERE t3.name = t.name 
    GROUP BY t3.name
  )
);

FIND A DEMO HERE

【讨论】:

【参考方案4】:

使用伪列 SCN_TO_TIMESTAMP(ORA_ROWSCN) SQL -

Merge into table_name t Using (select name, Address, Row_number() over (partition by name order by SCN_TO_TIMESTAMP(ORA_ROWSCN) desc) as rn From table_name) m On (t.name = m.name and m.rn = 1) When matched then set t.address = m.address;

【讨论】:

【参考方案5】:

有很多方法可以做到这一点(您的问题已经发布了三个答案)。

这是一种鲜为人知的方式。 (为什么它“鲜为人知”我不知道;许多开发人员错误地认为在 Oracle 中您无法通过联接进行更新。)

我展示了一个完整的示例,从create table 开始。与其他答案一样,我假设有一个 created_date 列告诉我们“最后”的含义。我还使用了许多开发人员忽略的另一个功能 - FIRST/LAST 聚合函数来查找最近的地址。

还要注意update 中的where 子句——它的存在是为了只包含实际更改地址的行。这样可以节省 undoredo 代。

create table t (name, address, created_date) as
  select 'Samual', '123 bixby'  , date '2015-03-24' from dual union all
  select 'Samual', '111 adelea' , date '2018-11-19' from dual union all
  select 'Arfan' , '12 spadina' , date '2015-07-28' from dual union all
  select 'Arfan' , '22 Spadina' , date '2017-03-10' from dual union all
  select 'Atesao', 'Roman Alley', date '2015-09-12' from dual union all
  select 'Atesao', 'Roman Alley', date '2018-01-14' from dual union all
  select 'Arfan' , '12 spadina' , date '2019-05-13' from dual
;

Table T created.

update
  (
    select address, last_address
    from   t join 
             ( select name, 
                      min(address) keep (dense_rank last order by created_date)
                        as last_address
               from   t
               group  by name
             )
             using (name)
  )
set address = last_address
where decode(address, last_address, 0) is null
;

2 rows updated.

那么,让我们看看它是否有效:

select * from t;

NAME   ADDRESS     CREATED_DATE  
------ ----------- --------------
Samual 111 adelea  3/24/2015     
Samual 111 adelea  11/19/2018    
Arfan  12 spadina  7/28/2015     
Arfan  12 spadina  3/10/2017     
Atesao Roman Alley 9/12/2015     
Atesao Roman Alley 1/14/2018     
Arfan  12 spadina  5/13/2019  

【讨论】:

看起来真的很棒,但只是一个问题,“FIRST/LAST 聚合函数”在哪里?老实说,这很有趣!【参考方案6】:

一旦您获得正确的数据,我建议您使用复合触发器来保持此类信息同步。 像这样的东西应该可以工作:

CREATE OR REPLACE TRIGGER cmp_addr_sync
FOR INSERT OR UPDATE ON table_name
COMPOUND TRIGGER 
DECLARE 
  TYPE lt_ChangedItem IS TABLE OF table_name.name%TYPE INDEXED BY VARCHAR2;
  l_tAddrChange lt_ChangedItem;
BEGIN
  AFTER EACH ROW IS
  BEGIN
    EXIT WHEN g_bSkipCheck; --THIS IS SOME BOOLEAN ELSEWHERE IN YOUR DB
    IF INSERTING THEN
      l_tAddrChange(:new.name) := :new.addr;
    ELSE
      IF :new.name <> :old.name OR :new.addr <> :old.addr THEN
        l_tAddrChange(:new.name) := :new.addr;
    END IF;
  END AFTER EACH ROW;

  AFTER STATEMENT IS
    l_sIndex table_name.name%TYPE;
  BEGIN
    IF l_tAddrChange.COUNT > 0 THEN
      g_bSkipCheck := TRUE;

      l_sIndex := l_tAddrChange.FIRST;

      WHILE l_sIndex IS NOT NULL LOOP
        UPDATE table_name t
        SET t.addr = l_tAddrChange(l_sIndex)
        WHERE t.name = l_sIndex
        AND t.addr <> l_tAddrChange(l_sIndex);   

        l_sIndex := l_tAddrChange.NEXT(l_sIndex); 
      END LOOP;      

      g_bSkipCheck := FALSE;
    END IF;
  EXCEPTION WHEN OTHERS THEN
    g_bSkipCheck := FALSE;
  END AFTER STATEMENT;
END cmp_add_sync;

编辑:对不起,我忘了你不能在 VARCHAR 索引的关联数组上使用 FORALL。还更改了一些代码以使用 %TYPE。

【讨论】:

以上是关于SQL Oracle .. 更新表的最新值的主要内容,如果未能解决你的问题,请参考以下文章

来自连接表的 MySQL 更新语句(受该表的最新值限制)

Oracle SQL查询:根据时间检索每组的最新值[重复]

关于oracle中 根据一个表的主键数据同步更新另一个关联表的字段。

sql语句获取表中最新数据

Oracle:Sql根据同一表另一行的值更新同一表的行

最新排行!Oracle 连登榜首,SQL Server 已持续下滑 9 个月。。