Oracle:非键保留表应该是

Rob*_*Rob 7 oracle join update

当我尝试更新连接时,我收到“ORA-01779:无法修改映射到非键保留表的列”。我在网站上搜索并找到了很多关于保留密钥的含义以及为什么有必要的建议......但据我所知,我正在遵守该建议,但仍然出现错误。

我有两个表:

PG_LABLOCATION has, among other things, the columns:
"LABLOCID" NUMBER,
"DNSNAME" VARCHAR2(200 BYTE)

LABLOCID is the primary key, DNSNAME has a unique constraint

PG_MACHINE has, among other things, the columns:
"MACHINEID" NUMBER, 
"LABLOCID" NUMBER, 
"IN_USE" NUMBER(1,0) DEFAULT 0, 
"UPDATE_TIME" TIMESTAMP (6) DEFAULT '01-JAN-1970'

MACHINEID is a primary key
LABLOCID is a foreign key into LABLOCID in PG_LABLOCATION (its primary key)
Run Code Online (Sandbox Code Playgroud)

我正在运行的更新是:

update 
  (select mac.in_use, mac.update_time
     from pg_machine mac 
     inner join pg_lablocation loc
       on mac.lablocid = loc.lablocid
     where loc.dnsname = 'value'
       and '02-JAN-2013' > mac.update_time
  )
set in_use = 1 - MOD( 101, 2 ), update_time = '02-JAN-2013';
Run Code Online (Sandbox Code Playgroud)

我只更新一个表 (PG_MACHINE) 中的值,另一个表中的连接列是主键,这应该使它通过我的阅读保持键。我担心 where 子句会导致问题,但我尝试删除 mac.update_time 上的过滤器并得到相同的错误,并且 loc.dnsname 具有唯一约束。

更奇怪的是,我们和许多人一样,拥有一个开发环境和一个生产环境。我们完成了从 prod 到 dev 的完整架构和数据迁移。我已经查看了它们,它们具有相同的索引和约束。该查询在 dev 中有效,但在 prod 中生成上述错误。

所以两个问题:

1) 你能看出我的查询有什么问题吗?2)您能否建议我的开发和生产环境(例如服务器设置)之间可能会导致此错误的不同之处而不是另一个?

Vin*_*rat 9

如果满足以下条件,您可以在 Oracle 中更新联接

  1. 只更新一张基表
  2. 所有其他表都是键保留的:对于基表的每一行,它们中的每一个必须最多有一行。

更新视图的附加限制适用)

在您的示例中,您PG_MACHINE仅更新表。Oracle 必须确保对于该表的单行,只能找到另一行中的一行。情况似乎是这样,因为您在PG_LABLOCATION.LABLOCID. 因此,您应该能够更新联接。例如,请参阅具有类似设置的SQLFiddle

在您的情况下,您应该:

  • 确保主键已启用、已验证、不可延迟(有趣的是,可延迟约束会阻止 Oracle 更新连接!)
  • 使用MERGEifPG_LABLOCATION.LABLOCID对于相关查询是唯一的。MERGE没有使用连接更新那么严格,并且只有在结果集中实际上存在重复时才会返回错误(而UPDATE如果有重复的可能性,则会失败)。
  • 检查您的查询,因为您不需要SELECT子句中父表中的值,您可以将其重写为半连接(保证不会生成重复项):

    UPDATE (SELECT mac.in_use, mac.update_time
              FROM pg_machine mac
             WHERE mac.lablocid IN (SELECT loc.lablocid 
                                      FROM pg_lablocation loc 
                                     WHERE loc.dnsname = 'value')
               AND to_date('02-JAN-2013') > mac.update_time)
       SET in_use = 1 - MOD(101, 2), 
           update_time = to_date('02-JAN-2013');
    
    Run Code Online (Sandbox Code Playgroud)

    这可以改写为:

    UPDATE pg_machine mac
       SET in_use = 1 - MOD(101, 2), 
           update_time = to_date('02-JAN-2013')
     WHERE mac.lablocid IN (SELECT loc.lablocid 
                              FROM pg_lablocation loc 
                             WHERE loc.dnsname = 'value')
       AND to_date('02-JAN-2013') > mac.update_time;
    
    Run Code Online (Sandbox Code Playgroud)

在这种情况下,我会选择第三个选项:通常您不能在 parent-child join 中更新父级