当前位置: 首页 > 面试题库 >

Oracle中看似关键的保留视图的更新引发了ORA-01779

邢昂然
2023-03-14
问题内容

问题

我正在尝试将性能不佳的MERGE语句重构为UPDATEOracle 12.1.0.2.0中的语句。该MERGE语句如下所示:

MERGE INTO t
USING (
  SELECT t.rowid rid, u.account_no_new
  FROM t, u, v
  WHERE t.account_no = u.account_no_old
  AND t.contract_id = v.contract_id
  AND v.tenant_id = u.tenant_id
) s
ON (t.rowid = s.rid)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new

它主要是低性能的,因为对大型(100M行)表有两次昂贵的访问 t

架构图

这些是涉及的简化表:

  • t``account_no正在迁移其列的目标表。
  • u含有迁移指令表account_no_oldaccount_no_new映射
  • v辅助表,用于模拟contract_id和之间的一对一关系tenant_id

该架构为:

CREATE TABLE v (
  contract_id NUMBER(18) NOT NULL PRIMARY KEY,
  tenant_id NUMBER(18) NOT NULL
);
CREATE TABLE t (
  t_id NUMBER(18) NOT NULL PRIMARY KEY,
  -- tenant_id column is missing here
  account_no NUMBER(18) NOT NULL,
  contract_id NUMBER(18) NOT NULL REFERENCES v
);
CREATE TABLE u (
  u_id NUMBER(18) NOT NULL PRIMARY KEY,
  tenant_id NUMBER(18) NOT NULL,
  account_no_old NUMBER(18) NOT NULL,
  account_no_new NUMBER(18) NOT NULL,

  UNIQUE (tenant_id, account_no_old)
);

我无法修改架构。我知道添加t.tenant_id将通过阻止JOIN来解决问题v

替代MERGE不起作用:

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

注意,无法避免自我联接,因为这种替代的等效查询会导致ORA-38104:

MERGE INTO t
USING (
  SELECT u.account_no_old, u.account_no_new, v.contract_id
  FROM u, v
  WHERE v.tenant_id = u.tenant_id
) s
ON (t.account_no = s.account_no_old AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new

UPDATE视图不起作用:

ORA-01779:无法修改映射到非键保留表的列

凭直觉,我将在此处应用传递闭包,这应确保in中的每个更新t行inu和in中最多只能有1行v。但是显然,Oracle无法识别这一点,因此以下UPDATE语句不起作用:

UPDATE (
  SELECT t.account_no, u.account_no_new
  FROM t, u, v
  WHERE t.account_no = u.account_no_old
  AND t.contract_id = v.contract_id
  AND v.tenant_id = u.tenant_id
)
SET account_no = account_no_new

以上提出ORA-01779。添加未记录的提示/*+BYPASS_UJVC*/似乎在12c上不再起作用。

如何告诉Oracle视图是关键的?

在我看来,该视图仍然是关键的保护,即,对于每行t恰好在 其中一行v,因此 最多
在一行u。因此,该观点应该是可更新的。有什么方法可以重写此查询以使Oracle相信我的判断?

还是有其他我忽略的语法阻止了该MERGE语句的双重访问t


问题答案:

有什么方法可以重写此查询以使Oracle相信我的判断?

通过在目标中引入帮助器列,我设法“说服” Oracle进行合并:

MERGE INTO (SELECT (SELECT t.account_no FROM dual) AS account_no_temp,
                    t.account_no, t.contract_id 
            FROM t) t
USING (
  SELECT u.account_no_old, u.account_no_new, v.contract_id
  FROM u, v
  WHERE v.tenant_id = u.tenant_id
) s
ON (t.account_no_temp = s.account_no_old AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;

db <>
fiddle演示

编辑

上面的想法的变体-子查询直接移至ON部分:

MERGE INTO (SELECT t.account_no, t.contract_id FROM t) t
USING (
      SELECT u.account_no_old, u.account_no_new, v.contract_id
      FROM u, v
      WHERE v.tenant_id = u.tenant_id
    ) s
ON ((SELECT t.account_no FROM dual) = s.account_no_old
     AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;

db <> fiddle
demo2

相关文章:ON子句中引用的列无法更新

编辑2:

MERGE INTO (SELECT t.account_no, t.contract_id FROM t) t
USING (SELECT u.account_no_old, u.account_no_new, v.contract_id
       FROM u, v
       WHERE v.tenant_id = u.tenant_id) s
ON((t.account_no,t.contract_id,'x')=((s.account_no_old,s.contract_id,'x')) OR 1=2) 
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;

db <> fiddle
demo3



 类似资料:
  • 问题内容: 保留哪些JavaScript关键字(函数名称,变量等)? 问题答案: 其中包含JavaScript中所有保留的关键字,专门针对那些当下保持诚实,而不仅仅是尝试得分的人:

  • 问题内容: 我有在同一活动中多次动态添加的 自定义 。 每个自定义视图实例在其他东西旁边都有一个孩子。该有它的 ID ,它是同每个视图的实例。 对于每个自定义视图实例,我在创建活动时都设置了不同的内容。当我旋转屏幕时,将重新创建活动,但是现在每个活动都显示相同(文本与最后一个实例的文本相同)。 这是因为实例状态的保存和还原会自动保存一些信息,但是该信息显然与ID链接,在我的情况下会产生问题。 有没

  • 问题内容: 可以说我有两个表,和。 我创建如下视图: 如果执行以下操作: 如果是index和index ,那么我们应该可以通过两个索引搜索来做到这一点。 但是,如果我可以在一个索引中对两个表建立索引,甚至对视图建立索引,那都会很好,如果源表(或)发生更改,该方法可以立即自动更新。 在Oracle中有没有办法做到这一点? 问题答案: 我不能与Oracle相提并论,但是我相信物化视图可以做到这一点。

  • 问题内容: 我有一些正在使用Gson反序列化的JSON。 我的问题是这是Java关键字,那么我该如何在类中创建与JSON中的字段相关的字段? 问题答案: 您可以使用gson的字段命名支持为字段使用其他名称。

  • 保留字是 Python 语言中一些已经被赋予特定意义的单词,这就要求开发者在开发程序时,不能用这些保留字作为标识符给变量、函数、类、模板以及其他对象命名。 Python 包含的保留字可以执行如下命令进行查看: >>> import keyword >>> keyword.kwlist ['False', 'None', 'True', 'and', 'as', 'assert', 'break',

  • 问题内容: 我有一个名为“ Group”的实体类,NetBeans警告我“实体表名称是保留的Java Persistence QL关键字”。 类似的情况是使用保留的SQL关键字。 这个名字会被转义吗?使用其他表名可以解决@Table(name =“ otherName”)的问题。还是应该重命名课程? 问题答案: 这个名字会被转义吗? 有 没有 在JPA规范是这么说的,如果您的提供商做,这是供应商特