Задача
Я пытаюсь реорганизовать неэффективный оператор MERGE
в оператор UPDATE
в Oracle 12.1.0.2.0. MERGE
оператор выглядит так:
MERGE INTO t
USING (
SELECT t.rowid rid, u.account_no_new
FROM t, u, v
WHERE t.account_no = u.account_no_old
AND t.contract_id = v.contract_id
AND v.tenant_id = u.tenant_id
) s
ON (t.rowid = s.rid)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new
Это в основном низкая производительность, потому что есть два дорогих доступа к большой (100M строк) таблице t
Схема
Это упрощенные таблицы:
t
Целевая таблица, столбец account_no
которой переносится.
u
Таблица инструкций переноса, содержащая отображение account_no_old
→ account_no_new
v
Вспомогательная таблица, моделирующая взаимное отношение между contract_id
и tenant_id
Схема:
CREATE TABLE v (
contract_id NUMBER(18) NOT NULL PRIMARY KEY,
tenant_id NUMBER(18) NOT NULL
);
CREATE TABLE t (
t_id NUMBER(18) NOT NULL PRIMARY KEY,
-- tenant_id column is missing here
account_no NUMBER(18) NOT NULL,
contract_id NUMBER(18) NOT NULL REFERENCES v
);
CREATE TABLE u (
u_id NUMBER(18) NOT NULL PRIMARY KEY,
tenant_id NUMBER(18) NOT NULL,
account_no_old NUMBER(18) NOT NULL,
account_no_new NUMBER(18) NOT NULL,
UNIQUE (tenant_id, account_no_old)
);
Я не могу изменить схему. Я знаю, что добавление t.tenant_id
решило бы проблему, запретив JOIN к v
Альтернатива MERGE не работает:
ORA-38104: столбцы, указанные в предложении ON, не могут быть обновлены
Обратите внимание, что самостоятельного объединения нельзя избежать, поскольку этот альтернативный эквивалентный запрос приводит к ORA-38104:
MERGE INTO t
USING (
SELECT u.account_no_old, u.account_no_new, v.contract_id
FROM u, v
WHERE v.tenant_id = u.tenant_id
) s
ON (t.account_no = s.account_no_old AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new
ОБНОВЛЕНИЕ не работает:
ORA-01779: невозможно изменить столбец, который сопоставляется с таблицей без сохранения ключа
Интуитивно я бы применил транзитивное замыкание здесь, что должно гарантировать, что для каждой обновленной строки в t
может быть только максимум 1 строка в u
и v
. Но, видимо, Oracle этого не распознает, поэтому следующий оператор UPDATE
не работает:
UPDATE (
SELECT t.account_no, u.account_no_new
FROM t, u, v
WHERE t.account_no = u.account_no_old
AND t.contract_id = v.contract_id
AND v.tenant_id = u.tenant_id
)
SET account_no = account_no_new
Вышеуказанное повышает ORA-01779
. Добавление недокументированной подсказки /*+BYPASS_UJVC*/
, похоже, больше не работает на 12c.
Как сообщить Oracle, что представление сохраняет ключи?
По моему мнению, представление все еще сохраняет ключи, то есть для каждой строки в t
, есть точно одна строка в v
, и, таким образом, самое большее один строка в u
. Таким образом, представление должно быть обновляемым. Есть ли способ переписать этот запрос, чтобы заставить Oracle доверять моему мнению?
Или есть какой-то другой синтаксис, который я пропускаю, который препятствует двойному доступу оператора MERGE
к t
?