Oracle
 sql >> Database >  >> RDS >> Oracle

ORA-01779:impossibile modificare una colonna che esegue il mapping a una tabella non conservata con chiave

Una clausola di espressione di tabella DML è utile solo quando sono necessarie colonne da più tabelle. Nel tuo caso, puoi utilizzare un aggiornamento regolare con un 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 hai davvero bisogno di utilizzare le colonne di entrambe le tabelle, hai tre opzioni:

  1. ripetere il join nel SET e il WHERE clausola. Questo è facile da costruire ma non ottimale.
  2. Espressione di tabella DML. Questo dovrebbe lavoro, se hai gli indici corretti.
  3. MERGE , di seguito è riportato un esempio.

    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');
    

Questo non risponde direttamente alla tua domanda, ma fornisce invece alcune soluzioni alternative. Non riesco a riprodurre l'errore che stai ricevendo. Avrei bisogno di un test case completo per approfondire.

Consigli generici per viste aggiornabili

Uno dei problemi principali delle viste aggiornabili è il gran numero di restrizioni sulle query che possono contenere. La query o la vista non deve contenere molte funzionalità, come DISTINCT, GROUP BY, determinate espressioni, ecc. Le query con tali funzionalità possono sollevare l'eccezione "ORA-01732:operazione di manipolazione dei dati non legale in questa vista".

La query della vista aggiornabile deve restituire in modo univoco ogni riga della tabella modificata solo una volta. La query deve essere "chiave preservata", il che significa che Oracle deve essere in grado di utilizzare una chiave primaria o un vincolo univoco per garantire che ogni riga venga modificata solo una volta.

Per dimostrare perché la chiave conservata è importante, il codice seguente crea un'istruzione di aggiornamento ambigua. Crea due tabelle, la prima tabella ha una riga e la seconda tabella ha due righe. Le tabelle si uniscono per la colonna A e prova ad aggiornare la colonna B nella prima tabella. In questo caso è bene che Oracle impedisca l'aggiornamento, altrimenti il ​​valore sarebbe non deterministico. A volte il valore sarebbe impostato su "1", a volte sarebbe impostato su "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;

Il MERGE dichiarazione non ha le stesse restrizioni. Il MERGE sembra che l'istruzione tenti di rilevare l'ambiguità in fase di esecuzione, anziché in fase di compilazione.

Sfortunatamente MERGE non sempre fa un buon lavoro nel rilevare l'ambiguità. In Oracle 12.2, l'istruzione seguente funzionerà occasionalmente e quindi avrà esito negativo. Apportare piccole modifiche alla query potrebbe farla funzionare o non riuscire, ma non riesco a trovare uno schema specifico.

--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 fallisce in fase di compilazione se è teoricamente possibile avere duplicati. Alcune affermazioni che dovrebbero il lavoro non verrà eseguito.

MERGE non riesce se il database rileva righe instabili in fase di esecuzione. Alcune affermazioni che non dovrebbero il lavoro continuerà.