Oracle -- 更新 ON 子句中引用的确切列

Posted

技术标签:

【中文标题】Oracle -- 更新 ON 子句中引用的确切列【英文标题】:Oracle -- Update the exact column referenced in the ON clause 【发布时间】:2017-10-03 19:46:23 【问题描述】:

我认为这个要求很少遇到,所以我无法搜索类似的问题。

我有一个需要更新 ID 的表。例如,table1 中的 ID 123 实际上应该是 456。我构建了一个单独的引用表来存储映射(例如,旧的 123 映射到新的 id 456)。

我使用了下面的查询,但显然它返回了错误 38104,在 ON 子句中引用的列无法更新。

MERGE INTO table1 
USING ref_table ON (table1.ID = ref_table.ID_Old)
WHEN MATCHED THEN UPDATE SET table.ID = ref_table.ID_New;

还有其他方法可以达到我的目的吗?

非常感谢您的回答!

【问题讨论】:

尝试在 ON 子句中使用 rowid @BarbarosÖzhan 他们是两个不同的表,rowid 不匹配 @DaveCosta 好的,你说得对,我不应该给出简短的回答。详情请参阅下方 MT0 的回答。 【参考方案1】:

使用ROWID 伪列:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE TABLE1( ID ) AS
  SELECT 1 FROM DUAL UNION ALL
  SELECT 2 FROM DUAL UNION ALL
  SELECT 3 FROM DUAL;

CREATE TABLE REF_TABLE( ID_OLD, ID_NEW ) AS
  SELECT 1, 4 FROM DUAL UNION ALL
  SELECT 2, 5 FROM DUAL;

MERGE INTO TABLE1 dst
USING ( SELECT t.ROWID AS rid,
               r.id_new
        FROM   TABLE1 t
               INNER JOIN REF_TABLE r
               ON ( t.id = r.id_old ) ) src
ON ( dst.ROWID = src.RID )
WHEN MATCHED THEN
  UPDATE SET id = src.id_new;

查询 1

SELECT * FROM table1

Results

| ID |
|----|
|  4 |
|  5 |
|  3 |

【讨论】:

如果 REF_TABLE 允许新 id 等于旧 id,您可能需要在最后添加 WHERE id != src.id_new 以避免更新不需要它的行。 (这也将避免尝试将 id 设置为 NULL,如果在 ID_NEW 列中允许 NULL;对旧表的 PK 列的此类更新无论如何都会被 PK 约束拒绝。)【参考方案2】:

您不能更新MERGEON 子句中使用的列。但是,如果您不需要进行MERGE 允许的其他更改,例如WHEN NOT MATCHED 或删除等,您可以使用UPDATE 来实现此目的。

您提到这是一个需要更新的ID。这是一个使用标量子查询的示例。因为它是ID,所以假定UNIQUE ID_OLD 的值在REF_TABLE 中。我不确定每个行是否需要更新或只需要一个子集,因此在此处设置更新以仅更新具有REF_TABLE 值的行。

CREATE TABLE TABLE1(
  ID NUMBER
);

CREATE TABLE REF_TABLE(
  ID_OLD NUMBER,
  ID_NEW NUMBER
);

INSERT INTO TABLE1 VALUES (1);
INSERT INTO TABLE1 VALUES (2);
INSERT INTO TABLE1 VALUES (100);

INSERT INTO REF_TABLE VALUES (1,10);
INSERT INTO REF_TABLE VALUES (2,20);

初始状态:

SELECT * FROM TABLE1;

ID   
1    
2    
100  

然后制作UPDATE

UPDATE TABLE1
SET TABLE1.ID = (SELECT REF_TABLE.ID_NEW
                 FROM REF_TABLE
                 WHERE REF_TABLE.ID_OLD = ID)
WHERE TABLE1.ID IN (SELECT REF_TABLE.ID_OLD
                    FROM REF_TABLE);

2 rows updated.

并检查更改:

SELECT * FROM TABLE1;

ID   
10   
20   
100  

【讨论】:

向 WHERE 添加一个条件(以防 REF_TABLE 允许新 ID 等于旧 ID)-... where .... AND id != ref_table.id。这将避免更新实际上不需要更新的行。

以上是关于Oracle -- 更新 ON 子句中引用的确切列的主要内容,如果未能解决你的问题,请参考以下文章

SQL语句中,子句不能使用列别名问题

ORA-38104: 无法更新 ON 子句中引用的列: "A"."MAINID"

在 ON CONFLICT 子句中使用多个冲突目标

Oracle:有子句中的命名子查询

未找到列:1054 'on 子句中的未知列 'locations.id'

JOIN 的 ON 子句中引用的表的顺序是不是重要?