使用唯一数据更新时出现非密钥保留 table 错误

Non-key-preserved table error for update with unique data

我对 Oracle 中的密钥保留连接视图更新感到困惑(通常);当使用带有连接的 select 进行更新时,对于更新的 table 或嵌入的 select 语句,是否需要保留密钥 属性。

在下面的例子中:

SQL> create table tabX (x1 number, x2 number);

Table created.

SQL>  create table tabY (y1 number, y2 number);

Table created.

SQL> insert into tabx values (1,11);

1 row created.

SQL> insert into tabx values (2,12);

1 row created.

SQL> insert into taby values(1,21);

1 row created.

SQL>  insert into taby values(2,22);

1 row created.

SQL> commit;

Commit complete.

select 的输出是唯一的,但更新仍然失败。 select 和更新都没有歧义或重复 - 那么为什么它会失败?

SQL> select x2,y2 from tabx,taby where tabx.x1=taby.y1;

        X2         Y2
---------- ----------
        11         21
        12         22

SQL> update (select x2,y2 from tabx,taby where tabx.x1=taby.y1) set x2=y2;
update (select x2,y2 from tabx,taby where tabx.x1=taby.y1) set x2=y2
                                                           *
ERROR at line 1:
ORA-01779: cannot modify a column which maps to a non key-preserved table

你知道没有重复,但是当语句被解析时优化器不知道没有,更重要的是不可能,任何复制。解析器查看统计数据来决定如何工作,但通常不查看数据。更新语句可能会被缓存和重用,所以即使它现在看起来并没有重复,当相同的语句稍后再次从缓存中 运行 时,数据可能已经改变。

假设您添加了一个额外的行:

insert into taby values(2,222);

然后您的查询得到:

select x2,y2 from tabx,taby where tabx.x1=taby.y1;

        X2         Y2
---------- ----------
        11         21
        12         22
        12        222

现在更新有两个可能的值来设置两个 x2=12 值;它们应该都是 22,还是都是 222,或者两者之一? Oracle 无法知道什么是正确的,也无法选择应该使用前两个选项中的哪一个(当然也不能使用第三个)。

现在,这不是您的实际情况,但您需要告诉 Oracle 这种情况不会发生。此处提示错误的措辞;对于保存键的视图,必须有一个键。如果您使用主键或唯一键定义 tabY

create table tabY (y1 number primary key, y2 number);

insert into taby values(1,21);

insert into taby values(2,22);

那么不允许我编造的第三个插入,Oracle 知道这一点,并且可以将这些知识应用到更新中:

update (select x2,y2 from tabx,taby where tabx.x1=taby.y1) set x2=y2;

2 rows updated.

select x2,y2 from tabx,taby where tabx.x1=taby.y1;

        X2         Y2
---------- ----------
        21         21
        22         22

无论您有多少行 x1=2,您都希望它们全部更新为 x2=22。所以 x1 不需要是唯一的,你也不需要 UK/Pk。但是 y1 确实必须是唯一的,因此您知道 y2 单个 值用于 所有 x1=2 行。

What i don't understand is whether the view becomes key preserved based if the columns involved join are declared unique or the actual values that is getting updated.

它是列,而不是值。但我认为我没有解释清楚的一点是,因为你正在做 set x2=y2 它可以看到只有 tabx table 实际上正在更新;所以它需要能够识别 tabx table 上的哪些行受到影响,然后它需要为这些行中的每一行计算 哪个 匹配taby 行以从中获取 y2 值 - 它从连接条件中获取。

对于每个要更新的 x2,它必须识别要使用的单个 y2 值,它通过查找 taby 行来实现,其中 taby.y1那一行的 tabx.x1。如果 taby 中有 - 或者可能有 - 多行匹配该条件,那么它将不知道要使用哪些选项 - 在我的示例中为 22 或 222。 taby 中必须有一个匹配项,因此 y1 必须是唯一的,并且必须通过 UK 或 PK 声明 是唯一的。

可能有很多不同的 x2 值被更新为相同的 y2 值;你也可以

insert into tabx values(2,13);
insert into tabx values(2,14);
etc.

并且所有这些仍将更新为相同的值 - 22 - 因为所有这些行中的非唯一 x1 值仍然映射到单个 ytab 行 tables UK/PK 通过连接条件。

如果您不想使用键,正如 AlexPoole 所建议的那样,您可以轻松地将其转换为 MERGE 语句(我们每次在更新视图时遇到 "non-key-preserved" 错误时都会尝试这样做) :

MERGE 
 INTO tabx
USING taby
   ON (x1 = y1)
 WHEN MATCHED THEN
   UPDATE
      SET x2 = y2
;