使用唯一数据更新时出现非密钥保留 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
;
我对 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
;