更新每个组的第一行-Oracle

时间:2018-07-12 15:55:50

标签: oracle plsql sql-update updates

需要从另一个表的数据中仅更新一个表的每个组的第一行。

我需要使用表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中的值。

我是否为此目的使用等级??

1 个答案:

答案 0 :(得分:1)

您可以使用等级或密集等级(甚至行号)来标识“顶部”行,尽管如果实际数据中可能存在联系,则可能不需要考虑怎么做:

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<>fiddle demo