【问题标题】:ORA-38104 - What is workaround to updating column in ON clause in a MERGE?ORA-38104 - 在 MERGE 中更新 ON 子句中的列的解决方法是什么?
【发布时间】:2018-11-16 21:52:14
【问题描述】:

我有table FOO,在col N_RA_ID 中有一些ID 值,在col V_CUST_NUMBER 中有一些MANUAL% 值,在col N_RA_ID 中有一些匹配值,在col V_CUST_NUMBER 中有非手动值。这是示例数据-

我想在col N_RA_ID 上自我加入,只要有完全匹配的地方,我想update N_RA_IDnull WHERE V_CUST_NUMBER LIKE 'MANUAL%'

所以输出应该看起来像 -

我尝试使用合并但得到 ORA-38104。有人可以帮忙吗?

MERGE INTO FOO X 
USING  (
SELECT T1.V_CUST_NUMBER AS MAN_CUST,T1.N_RA_ID, T2.V_CUST_NUMBER
FROM FOO T1
JOIN FOO T2
ON T1.N_RA_ID = T2.N_RA_ID
AND UPPER(T1.V_CUST_NUMBER) NOT LIKE 'MANUAL%'

) Z
ON (X.N_RA_ID = Z.N_RA_ID)
WHEN MATCHED THEN UPDATE
SET X.N_RA_ID = null
WHERE UPPER(X.V_CUST_NUMBER) LIKE 'MANUAL%'


SQL Error: ORA-38104: Columns referenced in the ON Clause cannot be updated: "X"."N_RA_ID"
38104. 00000 -  "Columns referenced in the ON Clause cannot be updated: %s"
*Cause:    LHS of UPDATE SET contains the columns referenced in the ON Clause
*Action:

【问题讨论】:

  • 完全匹配是什么意思?你能分享你尝试使用合并的查询吗
  • @ikram - 完全匹配,我的意思是N_RA_ID self 加入的任何地方。我已经添加了我运行过的MERGE 语句。

标签: sql oracle sql-update oracle12c


【解决方案1】:

您可以使用exists () 检查进行简单更新:

update foo f1
set n_ra_id = null
where v_cust_number like 'MANUAL%'
and exists (
  select *
  from foo f2
  where f2.n_ra_id = f1.n_ra_id
  and v_cust_number not like 'MANUAL%'
);

主要更新首先过滤以“MANUAL”开头的所有行,exists() 内的子查询查找表中具有相同 ID 但不以“MANUAL”开头的任何行。如果没有这样的非手动行,则该子句为假,手动行保持不变;如果匹配,则更新该行。

演示:

select * from foo;

V_CUST_NU    N_RA_ID
--------- ----------
MANUAL033      17024
MANUAL034     589469
MANUAL035     589470
MANUAL036     589478
BHASAD        589478

update foo f1
set n_ra_id = null
where v_cust_number like 'MANUAL%'
and exists (
  select *
  from foo f2
  where f2.n_ra_id = f1.n_ra_id
  and v_cust_number not like 'MANUAL%'
);

1 row updated.

select * from foo;

V_CUST_NU    N_RA_ID
--------- ----------
MANUAL033      17024
MANUAL034     589469
MANUAL035     589470
MANUAL036           
BHASAD        589478

【讨论】:

    【解决方案2】:

    通常的解决方法是在USING 子句中选择T2.ROWID as RID,然后匹配N_RA_ID 而不是匹配ON X.ROWID = Z.RID

    像这样:

    MERGE INTO FOO X 
    USING  (
    SELECT T2.ROWID AS RID,           -- ADD THIS HERE
           T1.V_CUST_NUMBER AS MAN_CUST,T1.N_RA_ID, T2.V_CUST_NUMBER
    FROM FOO T1
    JOIN FOO T2
    ON T1.N_RA_ID = T2.N_RA_ID
    AND UPPER(T1.V_CUST_NUMBER) NOT LIKE 'MANUAL%'   
    ) Z
    ON (X.ROWID = Z.RID)              -- CHANGE THE "ON" CLAUSE LIKE THIS
    WHEN MATCHED THEN UPDATE
    SET X.N_RA_ID = null
    WHERE UPPER(X.V_CUST_NUMBER) LIKE 'MANUAL%'
    

    【讨论】:

    • 您的答案似乎是解决方法。我有一个问题,当您应用此条件ON (X.ROWID = Z.RID) 时,它是否会同时返回MANUALNON MANUAL 行?然后我们继续更新MANUAL%?对吗?
    • @ReeyaOberoi - 正确。此外,您实际上不需要在 Z 中选择 ANYTHING(USING 子句中的视图)除了 T2.ROWID as RID,因为您并没有真正使用任何其他数据位从表中 - 您所需要的只是找到匹配项的简单事实。 (这就是为什么 UPDATE 具有半连接条件的原因,如 Alex Poole 所示。)这里显示的解决方法更通用,但它适用于 MERGE 的其他应用程序。
    【解决方案3】:

    从 Oracle 18c 开始,存在一些可应用于此类 MERGE 语句 which outsmart the parser who checks for ORA-38104 preconditions 的变通方法。例如,您可以使用带有额外虚拟列比较的行值表达式:

    MERGE INTO FOO X 
    USING  (
      SELECT T1.V_CUST_NUMBER AS MAN_CUST,T1.N_RA_ID, T2.V_CUST_NUMBER
      FROM FOO T1
      JOIN FOO T2
      ON T1.N_RA_ID = T2.N_RA_ID
      AND UPPER(T1.V_CUST_NUMBER) NOT LIKE 'MANUAL%'
    ) Z
    ON ((X.N_RA_ID, 'dummy') = ((Z.N_RA_ID, 'dummy')))
    WHEN MATCHED THEN UPDATE
    SET X.N_RA_ID = null
    WHERE UPPER(X.V_CUST_NUMBER) LIKE 'MANUAL%'
    

    【讨论】:

      猜你喜欢
      • 2011-08-19
      • 2014-06-14
      • 1970-01-01
      • 2021-08-31
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      相关资源
      最近更新 更多