Использование подзапроса вместо имени таблицы в отчете Oracle Update
Мне нужно написать инструкцию обновления, в которой использовались несколько таблиц, чтобы определить, какие строки нужно обновлять, поскольку в Oracle несколько таблиц не разрешены. Следующий запрос вернет ошибку "ORA-00971: Missing SET keyword"
UPDATE
TABLE1 a,
TABLE2 b
SET
a.COL1 = 'VALUE'
WHERE
a.FK = b.PK
AND b.COL2 IN ('SET OF VALUES')
Поднимая синтаксис инструкции UPDATE на oracle, я нашел следующую ссылку которая показывает, что вы можете использовать подзапрос вместо таблицы имя.
Когда я попытался написать такой запрос, я получил "ORA-01779: не могу изменить столбец, который сопоставляется с таблицей, не содержащей ключа"
UPDATE
(
SELECT
a.COL1
FROM
TABLE1 a,
TABLE2 b
WHERE
a.FK = b.PK
AND b.COL2 IN ('SET OF VALUES')
) update_tbl
SET
update_tbl.COL1 = 'VALUE'
Я переписал запрос (показать ниже), используя инструкцию EXISTS, и он отлично работает, но все равно хотел бы знать, как это делается.
UPDATE
TABLE1 update_tbl
SET
update_tbl.COL1 = 'VALUE'
WHERE
EXISTS (
SELECT
1
FROM
TABLE1 a
TABLE2 b
WHERE
a.FK = b.PK
AND b.COL2 IN ('SET OF VALUES')
AND update_tbl.PK = a.PK
)
Спасибо!
-Nate
Ответы
Ответ 1
Другая опция:
UPDATE TABLE1 a
SET a.COL1 = 'VALUE'
WHERE a.FK IN
( SELECT b.PK FROM TABLE2 b
WHERE b.COL2 IN ('SET OF VALUES')
)
Ваш второй пример будет работать, если (a) представление включало объявленный PK таблицы TABLE1:
UPDATE
(
SELECT
a.COL1, a.PKCOL
FROM
TABLE1 a,
TABLE2 b
WHERE
a.FK = b.PK
AND b.COL2 IN ('SET OF VALUES')
) update_tbl
SET
update_tbl.COL1 = 'VALUE'
... и (b) TABLE1.FK был объявленным внешним ключом для TABLE2
(По объявлению я имею в виду, что ограничение существует и включено).
Ответ 2
Я считаю, что хороший, быстрый и последовательный способ превратить инструкцию SELECT в UPDATE - это сделать обновление на основе ROWID.
UPDATE
TABLE1
SET
COL1 = 'VALUE'
WHERE
ROWID in
(
SELECT
a.rowid
FROM
TABLE1 a,
TABLE2 b
WHERE
a.FK = b.PK
AND b.COL2 IN ('SET OF VALUES')
)
Итак, ваш внутренний запрос определяет строки для обновления.
Ответ 3
Синтаксис вашего примера хорош, но Oracle требует, чтобы подзапрос включал первичные ключи. Это довольно значительное ограничение.
В соответствующей заметке вы также можете использовать круглые скобки для использования 2 или более полей в инструкции IN, как в:
UPDATE
TABLE1 update_tbl
SET
update_tbl.COL1 = 'VALUE'
WHERE
(update_tbl.PK1, update_tbl.pk2) in(
select some_field1, some_field2
from some_table st
where st.some_fields = 'some conditions'
);
Ответ 4
Когда вы выполняете обновление, вы можете, очевидно, только сообщать системе об обновлении значения к одному новому значению - сообщать ему об обновлении "X" как "Y", так и "Z" не имеет смысла. Таким образом, когда вы основываете обновление на результате встроенного представления, Oracle выполняет проверку наличия достаточных ограничений, чтобы предотвратить изменение дважды измененного столбца.
В вашем случае я ожидаю, что TABLE2.PK фактически не является объявленным первичным ключом. Если вы поместите основное или уникальное ограничение на этот столбец, тогда вам будет хорошо идти.
Существует недокументированный намек на то, чтобы выполнить проверку целостности соединения обновлений, используемую внутренне Oracle, но я бы не советовал ее использовать.
Обходной путь для этого заключается в использовании оператора MERGE, который не подлежит такому же тесту.
Ответ 5
Я нашел то, что мне нужно здесь:
Полезные команды SQL
Мне нужно было обновить одну таблицу с результатом соединения
Я пробовал эти решения без успеха:(
Вот выдержка страницы, на которую я указал выше
Используя курсоры, я смог успешно выполнить задачу
Я уверен, что есть другое решение, но это работало так...
DECLARE
/* Output variables to hold the result of the query: */
a T1.e%TYPE;
b T2.f%TYPE;
c T2.g%TYPE;
/* Cursor declaration: */
CURSOR T1Cursor IS
SELECT T1.e, T2.f, T2.g
FROM T1, T2
WHERE T1.id = T2.id AND T1.e <> T2.f
FOR UPDATE;
BEGIN
OPEN T1Cursor;
LOOP
/* Retrieve each row of the result of the above query
into PL/SQL variables: */
FETCH T1Cursor INTO a, b;
/* If there are no more rows to fetch, exit the loop: */
EXIT WHEN T1Cursor%NOTFOUND;
/* Delete the current tuple: */
DELETE FROM T1 WHERE CURRENT OF T1Cursor;
/* Insert the reverse tuple: */
INSERT INTO T1 VALUES(b, a);
/* Here is my stuff using the variables to update my table */
UPDATE T2
SET T2.f = a
WHERE T2.id = c;
END LOOP;
/* Free cursor used by the query. */
CLOSE T1Cursor;
END;
.
run;
Примечание: не забудьте зафиксировать;-)
Ответ 6
Каждая строка в результирующем наборе запроса в вашем предложении UPDATE должна возвращаться к одной и только одной строке в таблице, которую вы пытаетесь обновить, и таким образом, что Oracle может следовать автоматически. Поскольку запрос действительно представляет собой представление, один из способов подумать об этом заключается в том, что Oracle должен иметь возможность присоединиться к представлению обратно в целевую таблицу, чтобы знать, какую строку обновить.
Это по существу означает, что вам нужно включить первичный ключ таблицы назначения в этот запрос. Возможно, вы сможете использовать и другие уникальные поля индексов, но я не могу гарантировать, что СУБД Oracle достаточно умен, чтобы это допускать.