Oracle在线视图导致更新表达式问题

时间:2019-03-28 19:48:08

标签: sql oracle sql-update case

此查询出现错误“ 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'

有没有办法将它们结合起来?如果第一个查询开始运行,则这三个查询都可以运行;如果不运行,则最后两个查询都可以运行?合并它们是否有意义,还是我最好将它们分开?

非常感谢任何输入。

1 个答案:

答案 0 :(得分:2)

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

  • 对联接视图的任何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表。