sql >> Database >  >> RDS >> Oracle

ORA-01779:kan geen kolom wijzigen die is toegewezen aan een niet-sleutelbewaarde tabel

Een clausule voor DML-tabelexpressie is alleen nuttig als u kolommen uit meer dan één tabel nodig hebt. In jouw geval kun je een regelmatige update gebruiken met een 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
    );

Als je echt kolommen uit beide tabellen moet gebruiken, heb je drie opties:

  1. herhaal de join in de SET en de WHERE clausule. Dit is eenvoudig te bouwen, maar niet optimaal.
  2. DML-tabelexpressie. Dit moet werken, als je de juiste indexen hebt.
  3. MERGE , hieronder is een voorbeeld.

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

Dit beantwoordt niet direct uw vraag, maar biedt in plaats daarvan enkele tijdelijke oplossingen. Ik kan de fout die je krijgt niet reproduceren. Ik heb een volledige testcase nodig om het verder te onderzoeken.

Algemeen advies voor bij te werken weergaven

Een van de belangrijkste problemen met bijwerkbare weergaven is het grote aantal beperkingen op de query's die ze kunnen bevatten. De zoekopdracht of weergave mag niet veel functies bevatten, zoals DISTINCT, GROUP BY, bepaalde uitdrukkingen, enz. Bij zoekopdrachten met deze functies kan de uitzondering "ORA-01732:bewerking van gegevensmanipulatie niet legaal in deze weergave" optreden.

De bijwerkbare weergavequery moet elke rij van de gewijzigde tabel slechts één keer ondubbelzinnig retourneren. De query moet 'sleutel behouden' zijn, wat betekent dat Oracle een primaire sleutel of unieke beperking moet kunnen gebruiken om ervoor te zorgen dat elke rij slechts één keer wordt gewijzigd.

Om aan te tonen waarom sleutelbehoud belangrijk is, maakt de onderstaande code een dubbelzinnige update-instructie. Er worden twee tabellen gemaakt, de eerste tabel heeft één rij en de tweede tabel heeft twee rijen. De tabellen komen bij elkaar door de kolom A , en probeer de kolom B . bij te werken in de eerste tafel. In dit geval is het goed dat Oracle de update verhindert, anders zou de waarde niet-deterministisch zijn. Soms wordt de waarde ingesteld op "1", soms wordt deze ingesteld op "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;

De MERGE verklaring heeft niet dezelfde beperkingen. De MERGE statement lijkt dubbelzinnigheid te detecteren tijdens runtime, in plaats van compileertijd.

Helaas MERGE niet altijd goed in het detecteren van ambiguïteit. Op Oracle 12.2 zal de onderstaande verklaring af en toe werken en vervolgens mislukken. Als u kleine wijzigingen aanbrengt in de query, kan deze werken of mislukken, maar ik kan geen specifiek patroon vinden.

--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 mislukt tijdens het compileren als het theoretisch mogelijk is om duplicaten te hebben. Enkele uitspraken die moeten werk gaat niet door.

MERGE mislukt als de database onstabiele rijen detecteert tijdens runtime. Enkele uitspraken die niet zouden moeten het werk gaat gewoon door.




  1. Openshift MYSQL-omgevingsvariabelen niet ingesteld

  2. Hoe de Where-clausule te gebruiken in Select-instructie in SQL Server - SQL Server / TSQL-zelfstudie, deel 109

  3. Maak int8[]-array in PostgreSQL C-functie

  4. Hoe een wachtwoord van een SQL-server te decoderen?