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
子句——它的存在是为了只包含实际更改地址的行。这样可以节省 undo
和 redo
代。
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 .. 更新表的最新值的主要内容,如果未能解决你的问题,请参考以下文章