Встроенное представление Oracle, вызывающее проблемы с выражением обновления

#sql #oracle #sql-update #case

#sql #Oracle #sql-обновление #случай

Вопрос:

Я получаю сообщение об ошибке «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. Этот первый не может работать, потому что вы пытаетесь обновить запрос, а не таблицу. Это похоже на попытку обновить представление. Я думаю, для первого вам нужно что-то вроде: ОБНОВИТЬ PMEQMT P УСТАНОВИТЬ P_DATEOUT = NULL, P_DATEIN = NULL ТАМ, ГДЕ СУЩЕСТВУЕТ (ВЫБЕРИТЕ 1 Из TRANSLOG TL, ГДЕ TL.ITEMNO = P.EQNO И TL.LOC_DESC = ‘E-IN SERVICE’)

2. Обновление Сьюзан выглядит хорошо. Но, к вашему сведению, вы действительно можете обновить подзапрос или представление — есть только ограничения. Здесь оптимизатор жалуется, что он не может гарантировать (используя первичные / уникальные ключи), что ваш подзапрос вернет уникальный набор строк. Смотрите, спросите Тома: asktom.oracle.com/pls/asktom /…

3. Я узнал кое-что новое. Спасибо. 🙂

Ответ №1:

Что касается вашего первого запроса. Для обновления объединенного представления (вложенный выбор превращается во внутреннее представление) должны быть выполнены следующие условия:

  • Любая INSERT , UPDATE или DELETE операция в представлении объединения может изменять только одну базовую таблицу за раз. (С вашим запросом все в порядке, вы обновляете только PMEQMT таблицу.)
  • Все обновляемые столбцы представления объединения должны соответствовать столбцам таблицы с сохраненным ключом. (И вот тут у вас возникают проблемы).

Второе условие означает, что каждая строка таблицы должна иметь только одну соответствующую строку в объединенной таблице (отношение «один к одному»).

Вы можете прочитать больше об этом здесь.

Чтобы обойти эту ошибку, вы можете переписать свою инструкцию update в инструкцию merge:

 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 таблице снова и снова.