Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

ORA-01779:não é possível modificar uma coluna que mapeia para uma tabela não preservada por chave


Uma cláusula de expressão de tabela DML só é útil quando você precisa de colunas de mais de uma tabela. No seu caso, você pode usar uma atualização regular com um EXISTS :
update web_userrole
set role = replace(role, 'FULL', 'READ')
where read_only <> 'Y'
    and exists
    (
        select 1/0
        from web_userdatasource
        where datasource = p_datasource
            and username = web_userrole.username
    );

Se você realmente precisa usar colunas de ambas as tabelas, você tem três opções:
  1. repita a junção no SET e o WHERE cláusula. Isso é fácil de construir, mas não é o ideal.
  2. Expressão de tabela DML. Isso deve funcionar, se você tiver os índices corretos.

  3. MERGE , abaixo está um exemplo.
    merge into web_userrole
    using
    (
        select distinct username
        from web_userdatasource
        where datasource = p_datasource
    ) web_userdatasource on
    (
        web_userrole.username = web_userdatasource.username
        and web_userrole.read_only <> 'Y'
    )
    when matched then update
    set role = replace(role, 'FULL', 'READ');
    

Isso não responde diretamente à sua pergunta, mas fornece algumas soluções alternativas. Não consigo reproduzir o erro que você está recebendo. Eu precisaria de um caso de teste completo para analisar melhor.

Aconselhamento genérico para visualizações atualizáveis


Um dos principais problemas das visualizações atualizáveis ​​é o grande número de restrições nas consultas que elas podem conter. A consulta ou visualização não deve conter muitos recursos, como DISTINCT, GROUP BY, certas expressões, etc. Consultas com esses recursos podem gerar a exceção "ORA-01732:operação de manipulação de dados não legal nesta visualização".

A consulta de exibição atualizável deve retornar inequivocamente cada linha da tabela modificada apenas uma vez. A consulta deve ser “preservada por chave”, o que significa que o Oracle deve ser capaz de usar uma chave primária ou restrição exclusiva para garantir que cada linha seja modificada apenas uma vez.

Para demonstrar por que a chave preservada é importante, o código abaixo cria uma instrução de atualização ambígua. Ele cria duas tabelas, a primeira tabela tem uma linha e a segunda tabela tem duas linhas. As tabelas se unem pela coluna A , e tente atualizar a coluna B na primeira tabela. Nesse caso é bom que o Oracle impeça a atualização, caso contrário o valor seria não determinístico. Às vezes, o valor seria definido como "1", às vezes, seria definido como "2".
--Create table to update, with one row.
create table test1 as
select 1 a, 1 b from dual;

--Create table to join two, with two rows that match the other table's one row.
create table test2 as
select 1 a, 1 b from dual union all
select 1 a, 2 b from dual;

--Simple view that joins the two tables.
create or replace view test_view as
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a;

--Note how there's one value of B_1, but two values for B_2.
select *
from test_view;

A  B_1  B_2
-  ---  ---
1    1    1
1    1    2

--If we try to update the view it fails with this error:
--ORA-01779: cannot modify a column which maps to a non key-preserved table
update test_view
set b_1 = b_2;

--Using a subquery also fails with the same error.
update
(
    select test1.a, test1.b b_1, test2.b b_2
    from test1
    join test2 on test1.a = test2.a
)
set b_1 = b_2;

O MERGE declaração não tem as mesmas restrições. O MERGE A instrução parece tentar detectar ambiguidade em tempo de execução, em vez de tempo de compilação.

Infelizmente MERGE nem sempre faz um bom trabalho na detecção de ambiguidade. No Oracle 12.2, a instrução abaixo funcionará ocasionalmente e falhará. Fazer pequenas alterações na consulta pode fazê-la funcionar ou falhar, mas não consigo encontrar um padrão específico.
--The equivalent MERGE may work and changes "2" rows, even though there's only one.
--But if you re-run, or uncomment out the "order by 2 desc" it might raise:
--  ORA-30926: unable to get a stable set of rows in the source tables
merge into test1
using
(
    select test1.a, test1.b b_1, test2.b b_2
    from test1
    join test2 on test1.a = test2.a
    --order by 2 desc
) new_rows
    on (test1.a = new_rows.a)
when matched then update set test1.b = new_rows.b_2;

UPDATE falha em tempo de compilação se for teoricamente possível ter duplicatas. Algumas declarações que devem trabalho não será executado.

MERGE falha se o banco de dados detectar linhas instáveis ​​em tempo de execução. Algumas declarações que não devem trabalho ainda será executado.