【问题标题】:UPDATE by matching ROW_NUMBER with another table?通过将 ROW_NUMBER 与另一个表匹配来更新?
【发布时间】:2018-07-26 07:55:32
【问题描述】:

这可以在 SQL Server 中完成(而我们可以在 UPDATE 语句中使用 FROM 子句)。但我对 Oracle SQL 还是很陌生。场景很简单,就像这样。我有 2 个表(具有相同的行数)。现在我需要通过匹配每个表的row_number,将这个表的一列更新为另一个表的一列。

我参考了 Oracle UPDATE,这看起来是最有前途的方法:

update 
(select * from 
 (select A1, 
         row_number() over (order by A1) as rn from A) d 
 join 
 (select B1, 
         row_number() over (order by B1) as rn from B) v on d.rn = v.rn)
set A1 = B1;

但它抛出了这样的错误:

无法修改映射到非键保留表的列

有一些像这样涉及 UPDATE 的例子,但它与我的场景不完全一样,因为匹配条件不涉及基表的任何现有列(我们需要更新的表)

【问题讨论】:

  • A1和B1是两张表的主键吗?
  • @KaushikNayak 不,它们只是相同数据类型的普通列。

标签: sql oracle sql-update


【解决方案1】:

理想情况下,表应该有一个主键或一个唯一键,以便识别行。如果两个表被认为是相关的,那么要么应该有一个相互链接的外键,要么应该有一组唯一标识记录的公共列。如果不是,它就违反了基本的 RDBMS 原则。 row_number() 是一个生成数字的函数,而不是表中存在的物理键。您应该考虑更改您的设计以利用有效的数据提取方法和更简单的查询。

作为一种解决方法,您可以使用 MERGE 语句 usingROWID。但是,请注意,如果行数不匹配或其他我想不到的情况,这可能无法按预期工作。

MERGE INTO a 
using (SELECT a.a1, 
              b.b1, 
              a.rid 
       FROM   (SELECT a.*, 
                      row_number() 
                        OVER ( 
                          ORDER BY a1 ) AS rn, 
                      a.rowid           AS rid 
               FROM   a) a 
              JOIN (SELECT b.*, 
                           row_number() 
                             OVER ( 
                               ORDER BY b1 ) AS rn 
                    FROM   b) b 
                ON ( a.rn = b.rn )) s 
ON ( a.rowid = s.rid ) 
WHEN matched THEN 
  UPDATE SET a.a1 = s.b1; 

Demo

这不是直接更新的原因是表中的行没有默认排序。因此,必须有一个像主键这样的键来为我们提供所需的顺序。

【讨论】:

  • 是的,谢谢你,这符合预期。这里的重点是使用rowid,这是任何表上都存在的一些内置隐藏列(不像row_number())。如果没有这种列,我们将无法在这种情况下更新表列(我之前已经找到了一些使用 MERGE 的解决方案,但它们与此不同)。另外这里的设计不是为了生产,我只是想更新数据用于测试目的(复制数据进行测试),当然我们总是需要标准的外键来链接表。
【解决方案2】:

您可以通过一系列语句来达到您想要的结果,但是没有直接的单一语句解决方案:

解决方案 1:更改表

--alter table to add a column for sequencing
alter table A
add(rownumber number);
alter table B
add(rownumber number);

--add sequencing
update A
set rownumber = rownum;
update B
set rownumber = rownum;

--update Data
update A
set A1 = (select B1 from B where A.rownumber = B.rownumber)

--undo DDL changes
alter table A
drop column rownumber;
alter table B
drop column rownumber;


解决方案 2:使用第三张表

CREATE global TEMPORARY TABLE ab 
  ( 
     a1 VARCHAR(10), 
     b1 VARCHAR(10) 
  ) 
ON COMMIT DELETE ROWS; 

INSERT INTO ab 
(SELECT a1, 
        b1 
 FROM   (SELECT a1, 
                Row_number() 
                  over ( 
                    ORDER BY a1) AS rna 
         FROM   a) xa 
        join (SELECT b1, 
                     Row_number() 
                       over ( 
                         ORDER BY b1) AS rnb 
              FROM   b) xb 
          ON xa.rna = xb.rnb); 

UPDATE a 
SET    a1 = (SELECT b1 
             FROM   ab 
             WHERE  ab.a1 = a.a1); 

DROP TABLE ab; 

如果您的 row_number 列与要更新的列不同,则使用 Oracle Merge 可能是一种解决方案,因为合并不支持更新用于 ON 子句的同一列。

【讨论】:

  • 实际上我正在搜索语句(可以是多个),但不应该是更改表语句。因为在某些情况下这是不可能的,而且我需要来自 SQL Server 的等效解决方案。
  • 不幸的是,AFAIK Oracle 不支持这一点,因为您的行排序列 A1/B1 是您尝试更新的列。如果您的 row_number 可以基于除您正在更新的列之外的任何其他列,那么它是可行的。
  • 添加了使用全局临时表的替代解决方案。
  • 在 SQL Server 中我们不需要更改/创建任何东西,只需正常查询即可。所以起初我一直在寻找这样的解决方案。然而看起来这是 Oracle UPDATE 设计的一个荒谬的限制,我们不能在这里享受轻松。如果没有其他更好的解决方案,我会考虑尽快接受您的回答。
  • 因为 Kaushik Nayak 提供的解决方案有效并且看起来更好,所以我会接受他的回答,但你的这个回答值得我一票。还是谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-02-16
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-18
相关资源
最近更新 更多