sql >> Databasteknik >  >> RDS >> Oracle

ORA-01779:kan inte ändra en kolumn som mappas till en icke-nyckelbevarad tabell

En DML-tabelluttryckssats är bara användbar när du behöver kolumner från mer än en tabell. I ditt fall kan du använda en vanlig uppdatering 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
    );

Om du verkligen behöver använda kolumner från båda tabellerna har du tre alternativ:

  1. upprepa sammanfogningen i SET och WHERE klausul. Detta är lätt att bygga men inte optimalt.
  2. DML-tabelluttryck. Detta bör fungerar, om du har rätt index.
  3. MERGE , nedan är ett exempel.

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

Detta svarar inte direkt på din fråga, utan ger istället några lösningar. Jag kan inte återskapa felet du får. Jag skulle behöva ett fullständigt testfall för att undersöka det närmare.

Allmänna råd för uppdateringsbara vyer

Ett av huvudproblemen med uppdateringsbara vyer är det stora antalet begränsningar för de frågor de kan innehålla. Frågan eller vyn får inte innehålla många funktioner, såsom DISTINCT, GROUP BY, vissa uttryck, etc. Förfrågningar med dessa funktioner kan ge upphov till undantaget "ORA-01732:datamanipulering är inte laglig i denna vy".

Den uppdateringsbara vyfrågan måste otvetydigt returnera varje rad i den ändrade tabellen endast en gång. Frågan måste vara "nyckelbevarad", vilket innebär att Oracle måste kunna använda en primärnyckel eller unik begränsning för att säkerställa att varje rad endast ändras en gång.

För att visa varför nyckelbevarad är viktig skapar koden nedan en tvetydig uppdateringssats. Den skapar två tabeller, den första tabellen har en rad och den andra tabellen har två rader. Tabellerna sammanfogas av kolumnen A , och försök uppdatera kolumnen B i den första tabellen. I det här fallet är det bra att Oracle förhindrar uppdateringen, annars skulle värdet vara icke-deterministiskt. Ibland skulle värdet sättas till "1", ibland skulle det sättas till "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 uttalandet har inte samma begränsningar. MERGE satsen verkar försöka upptäcka tvetydighet vid körning, istället för kompileringstid.

Tyvärr MERGE gör inte alltid ett bra jobb med att upptäcka oklarheter. På Oracle 12.2 kommer uttalandet nedan att fungera ibland och sedan misslyckas. Att göra små ändringar i frågan kan få den att fungera eller misslyckas, men jag kan inte hitta ett 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 misslyckas vid kompilering om det är teoretiskt möjligt att ha dubbletter. Några påståenden som bör arbetet kommer inte att köras.

MERGE misslyckas om databasen upptäcker instabila rader vid körning. Vissa uttalanden som inte borde arbetet kommer fortfarande att köras.




  1. MySql-frågegruppering efter tid

  2. Kalendertabeller i PostgreSQL 9

  3. Ta bort dubbletter av rader i en tabell

  4. Plan Explorer 3.0-webbseminarium – exempel och frågor och svar