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

ORA-01779:kan ikke ændre en kolonne, der er knyttet til en ikke-nøglebevaret tabel

En DML-tabeludtryksklausul er kun nyttig, når du har brug for kolonner fra mere end én tabel. I dit tilfælde kan du bruge en almindelig opdatering med en 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
    );
 

Hvis du virkelig har brug for at bruge kolonner fra begge tabeller, har du tre muligheder:

  1. gentag joinforbindelsen i SET og WHERE klausul. Dette er nemt at bygge, men ikke optimalt.
  2. DML-tabeludtryk. Dette skal arbejde, hvis du har de korrekte indekser.
  3. MERGE , nedenfor er et eksempel.

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

Dette svarer ikke direkte på dit spørgsmål, men giver i stedet nogle løsninger. Jeg kan ikke gengive den fejl, du får. Jeg har brug for en komplet testcase for at se nærmere på det.

Generiske råd til opdaterbare visninger

Et af hovedproblemerne med opdaterbare visninger er det store antal begrænsninger på de forespørgsler, de kan indeholde. Forespørgslen eller visningen må ikke indeholde en masse funktioner, såsom DISTINCT, GROUP BY, visse udtryk osv. Forespørgsler med disse funktioner kan medføre undtagelsen "ORA-01732:datamanipulation er ikke lovlig på denne visning".

Den opdaterbare visningsforespørgsel skal entydigt returnere hver række i den ændrede tabel kun én gang. Forespørgslen skal være "nøglebevaret", hvilket betyder, at Oracle skal kunne bruge en primær nøgle eller en unik begrænsning for at sikre, at hver række kun ændres én gang.

For at demonstrere, hvorfor nøglebevaring er vigtig, opretter nedenstående kode en tvetydig opdateringserklæring. Den opretter to tabeller, den første tabel har en række og den anden tabel har to rækker. Tabellerne forbindes af kolonnen A , og prøv at opdatere kolonnen B i den første tabel. I dette tilfælde er det godt, at Oracle forhindrer opdateringen, ellers ville værdien være ikke-deterministisk. Nogle gange ville værdien blive sat til "1", nogle gange ville den blive sat til "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;

MERGE erklæring har ikke de samme begrænsninger. MERGE sætningen ser ud til at forsøge at detektere tvetydighed under kørsel i stedet for kompileringstid.

Desværre MERGE ikke altid gør et godt stykke arbejde med at opdage tvetydighed. På Oracle 12.2 vil nedenstående erklæring af og til fungere og derefter mislykkes. Hvis du laver små ændringer i forespørgslen, kan det få det til at fungere eller mislykkes, men jeg kan ikke finde et specifikt mønster.

--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 mislykkes på kompileringstidspunktet, hvis det teoretisk er muligt at have dubletter. Nogle udsagn, der bør arbejdet vil ikke køre.

MERGE mislykkes, hvis databasen registrerer ustabile rækker under kørsel. Nogle udsagn, som ikke burde arbejdet vil stadig køre.




  1. Hvordan fjerner man nye linjetegn fra datarækker i mysql?

  2. MySQL Entity Framework 4.0 Stored Procedure Field Mapping

  3. Hvordan sender jeg en jdbc-paramater ved hjælp af springboot og dvale?

  4. Sådan fremskyndes indsættelsesydelsen i PostgreSQL