【问题标题】:Oracle In-line View Causing Update Expression IssuesOracle 内联视图导致更新表达式问题
【发布时间】:2019-03-28 19:48:07
【问题描述】:

我收到错误“ORA-01779:无法修改映射到非键保留表的列”:

UPDATE 
 (SELECT P.SERVICE_DATE_OUT AS P_DATEOUT, P.SERVICE_DATE_IN AS P_DATEIN
  FROM TRANSLOG TL JOIN PMEQMT P ON TL.ITEMNO = P.EQNO
  WHERE TL.LOC_DESC = 'E-IN SERVICE')
SET P_DATEOUT = NULL, P_DATEIN = NULL

经过研究,我认为此错误是由于创建内联视图和更新尝试更新两个表而不是我想要的表?谁能证实这一点?有解决办法吗?

为了进一步解释我的场景,我已经构建了另外两个查询,这些查询将在上述查询运行之前每天运行(如果我可以让某些东西正常运行)。

第一:

  UPDATE PMEQMT P
   SET SERVICE_DATE_OUT = (SELECT MAX(TL.TRANS_DATE)
                        FROM TRANSLOG TL
                        WHERE P.EQNO = TL.ITEMNO AND 
                              TL.LOC_DESC = 'E-OUT OF SERVICE' AND
                              TL.TRANS_DATE >= SYSDATE - 1 AND
                              TL.TRANS_IN_OUT = 'IN'
                       )
WHERE P.CLASS = 'TL' AND
      P.SERVICE_DATE_OUT IS NULL

第二:

UPDATE PMEQMT P
   SET SERVICE_DATE_IN =
   CASE 
   WHEN SERVICE_DATE_IN IS NULL THEN (SELECT MAX(TL.TRANS_DATE)
                        FROM TRANSLOG TL
                        WHERE P.EQNO = TL.ITEMNO AND 
                              TL.LOC_DESC = 'E-IN SERVICE' AND
                              TL.TRANS_DATE >= SYSDATE - 1 AND
                              TL.TRANS_IN_OUT = 'IN'
                       )
   WHEN (TRUNC(SERVICE_DATE_IN)) <= (TRUNC(SYSDATE)) THEN (SELECT ((TRUNC(SYSDATE))+1)
                        FROM TRANSLOG TL
                        WHERE P.EQNO = TL.ITEMNO AND
                        TL.LOC_DESC = 'E-OUT OF SERVICE'
                       )
   WHEN (TRUNC(SERVICE_DATE_IN)) > (TRUNC(SYSDATE)) THEN (SELECT SERVICE_DATE_IN 
                        FROM TRANSLOG TL
                        WHERE P.EQNO = TL.ITEMNO AND
                        TL.LOC_DESC = 'E-OUT OF SERVICE'
                       )
   END
WHERE CLASS = 'TL'

有没有办法把这些结合起来?如果第一个查询起作用,那么所有三个都可以运行,如果没有,那么最后两个?将它们结合起来是否有意义,或者我最好将它们分开?

非常感谢任何输入。

【问题讨论】:

  • 第一个无法工作,因为您正在尝试更新查询而不是表。这就像尝试更新视图。我认为,对于第一个,您需要类似: UPDATE PMEQMT P SET P_DATEOUT = NULL, P_DATEIN = NULL WHERE EXISTS (SELECT 1 FROM TRANSLOG TL WHERE TL.ITEMNO = P.EQNO AND TL.LOC_DESC = 'E-IN SERVICE ')
  • Susan 的更新看起来不错。但仅供参考,您实际上可以更新子查询或视图 - 只是有限制。在这里,优化器抱怨它不能保证(使用主键/唯一键)您的子查询将返回一组唯一的行。看到这个问汤姆:asktom.oracle.com/pls/asktom/…
  • 我学到了一些新东西。谢谢你。 :)

标签: sql oracle sql-update case


【解决方案1】:

关于您的第一个查询。为了更新一个连接视图(嵌套的选择在内部变成一个视图),必须满足以下条件:

  • 对连接视图的任何INSERTUPDATEDELETE 操作一次只能修改一个基础基表。 (您的查询没问题,您只更新 PMEQMT 表。)
  • 连接视图的所有可更新列都必须映射到key-preserved table 的列。 (这就是你遇到麻烦的地方)。

第二个条件意味着表的每一行在连接表中必须只有一个对应的行(一对一的关系)。

您可以阅读更多关于它的信息here

要解决此错误,您可以将更新语句重写为合并语句:

merge into PMEQMT t
using (SELECT P.EQNO
          FROM TRANSLOG TL JOIN PMEQMT P ON TL.ITEMNO = P.EQNO
       WHERE TL.LOC_DESC = 'E-IN SERVICE') u
on (u.EQNO = t.EQNO)
when matched then update set t.P_DATEOUT = NULL, t.P_DATEIN = NULL;

关于最后两个查询,您可以将它们合并为一个查询:

UPDATE PMEQMT P
   SET SERVICE_DATE_OUT = case when P.SERVICE_DATE_OUT IS NULL then(SELECT MAX(TL.TRANS_DATE)
                                                                    FROM TRANSLOG TL
                                                                    WHERE P.EQNO = TL.ITEMNO AND 
                                                                          TL.LOC_DESC = 'E-OUT OF SERVICE' AND
                                                                          TL.TRANS_DATE >= SYSDATE - 1 AND
                                                                          TL.TRANS_IN_OUT = 'IN'
                                                                   ) 
                          else P.SERVICE_DATE_OUT end,
       SERVICE_DATE_IN =
               CASE 
               WHEN SERVICE_DATE_IN IS NULL THEN (SELECT MAX(TL.TRANS_DATE)
                                    FROM TRANSLOG TL
                                    WHERE P.EQNO = TL.ITEMNO AND 
                                          TL.LOC_DESC = 'E-IN SERVICE' AND
                                          TL.TRANS_DATE >= SYSDATE - 1 AND
                                          TL.TRANS_IN_OUT = 'IN'
                                   )
               WHEN (TRUNC(SERVICE_DATE_IN)) <= (TRUNC(SYSDATE)) THEN (SELECT ((TRUNC(SYSDATE))+1)
                                    FROM TRANSLOG TL
                                    WHERE P.EQNO = TL.ITEMNO AND
                                    TL.LOC_DESC = 'E-OUT OF SERVICE'
                                   )
               WHEN (TRUNC(SERVICE_DATE_IN)) > (TRUNC(SYSDATE)) THEN (SELECT SERVICE_DATE_IN 
                                    FROM TRANSLOG TL
                                    WHERE P.EQNO = TL.ITEMNO AND
                                    TL.LOC_DESC = 'E-OUT OF SERVICE'
                                   )
               END     
WHERE P.CLASS = 'TL'; 

一个对数据库的请求总是比几个连续的更好。也可以考虑重写内部重复查询,这样就可以一次性得到结果,而不用一次次点击TRANSLOG表。

【讨论】:

    猜你喜欢
    • 2022-08-23
    • 2010-10-08
    • 1970-01-01
    • 2014-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    相关资源
    最近更新 更多