首页 > 解决方案 > 更新每组的第一行 - Oracle

问题描述

需要从其他表的数据中只更新一个表的每组的顶行。

我需要使用表 B 中的详细信息更新表 A

Table A
---------
ID Name   Date      PCNO
 1  abc  1/1/12      123
 2  def  1/1/12      234 
 3  fgh  1/2/12      222
 4   asd 1/2/12      234

TABLE B
-----------
ID Name   Date       PCNO
1   adsf  1/1/12      4343
2   sdf   1/2/12      9347

对于按“日期”分组并按 PCNO desc 排序的表 A 的每个顶部记录,我想更新表 B 中的值。

我是否为此目的使用排名。

标签: oracleplsqlsql-updateupdates

解决方案


您可以使用等级或密集等级(甚至行号)来识别“顶部”行,但您可能不需要考虑如果在您的真实数据中可能存在联系,该怎么办:

select a.id, a.name, a.date_col, a.pcno,
  dense_rank() over (partition by date_col order by pcno desc) as rnk
from table_a a;

        ID NAME DATE_COL         PCNO        RNK
---------- ---- ---------- ---------- ----------
         2 def  2012-01-01        234          1
         1 abc  2012-01-01        123          2
         4 asd  2012-01-02        234          1
         3 fgh  2012-01-02        222          2

您可以加入表 B 以获得排名靠前的新值:

select a.id, a.name, a.date_col, a.pcno,
  dense_rank() over (partition by a.date_col order by a.pcno desc) as rnk,
  case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
       then b.name else a.name end as new_name,
  case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
       then b.pcno else a.pcno end as new_pcno
from table_a a
join table_b b on b.date_col = a.date_col;

        ID NAME DATE_COL         PCNO        RNK NEW_   NEW_PCNO
---------- ---- ---------- ---------- ---------- ---- ----------
         2 def  2012-01-01        234          1 adsf       4343
         1 abc  2012-01-01        123          2 abc         123
         4 asd  2012-01-02        234          1 sdf        9347
         3 fgh  2012-01-02        222          2 fgh         222

然后您可以在合并语句中使用它:

merge into table_a target
using (
  select a.id, a.name, a.date_col, a.pcno,
    dense_rank() over (partition by a.date_col order by a.pcno desc) as rnk,
    case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
         then b.name else a.name end as new_name,
    case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
         then b.pcno else a.pcno end as new_pcno
  from table_a a
  join table_b b on b.date_col = a.date_col
) source
on (source.id = target.id)
when matched then update
set target.name = source.new_name, target.pcno = source.new_pcno
where source.rnk = 1;

或许

merge into table_a target
using (
  select a.id, a.name, a.date_col, a.pcno,
    case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
         then b.name else a.name end as new_name,
    case when dense_rank() over (partition by a.date_col order by a.pcno desc) = 1
         then b.pcno else a.pcno end as new_pcno
  from table_a a
  join table_b b on b.date_col = a.date_col
) source
on (source.id = target.id)
when matched then update
set target.name = source.new_name, target.pcno = source.new_pcno
where target.name != source.new_name or target.pcno != source.new_pcno;

其中任何一个报告2 rows merged,然后:

select * from table_a;

        ID NAME DATE_COL         PCNO
---------- ---- ---------- ----------
         1 abc  2012-01-01        123
         2 adsf 2012-01-01       4343
         3 fgh  2012-01-02        222
         4 sdf  2012-01-02       9347

如果某个日期并不总是匹配,您可能需要对其进行调整,尽管内部连接应该负责这一点。

db<>小提琴演示


推荐阅读