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