Oracle
 sql >> Base de données >  >> RDS >> Oracle

ORA-01779 :impossible de modifier une colonne qui correspond à une table dont la clé n'est pas préservée

Une clause d'expression de table DML n'est utile que lorsque vous avez besoin de colonnes de plusieurs tables. Dans votre cas, vous pouvez utiliser une mise à jour régulière avec 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
    );

Si vous avez vraiment besoin d'utiliser les colonnes des deux tables, vous avez trois options :

  1. répéter la jointure dans le SET et le WHERE clause. C'est facile à construire mais pas optimal.
  2. Expression de table DML. Cela devrait fonctionner, si vous avez les index corrects.
  3. MERGE , ci-dessous un exemple.

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

Cela ne répond pas directement à votre question, mais fournit plutôt des solutions de contournement. Je ne peux pas reproduire l'erreur que vous obtenez. J'aurais besoin d'un cas de test complet pour approfondir la question.

Conseils génériques pour les vues pouvant être mises à jour

L'un des principaux problèmes des vues pouvant être mises à jour est le grand nombre de restrictions sur les requêtes qu'elles peuvent contenir. La requête ou la vue ne doit pas contenir beaucoup de fonctionnalités, telles que DISTINCT, GROUP BY, certaines expressions, etc. Les requêtes avec ces fonctionnalités peuvent générer l'exception "ORA-01732 :opération de manipulation de données non légale sur cette vue".

La requête de vue pouvant être mise à jour doit renvoyer sans ambiguïté chaque ligne de la table modifiée une seule fois. La requête doit être "à clé préservée", ce qui signifie qu'Oracle doit pouvoir utiliser une clé primaire ou une contrainte unique pour s'assurer que chaque ligne n'est modifiée qu'une seule fois.

Pour démontrer pourquoi la clé conservée est importante, le code ci-dessous crée une instruction de mise à jour ambiguë. Il crée deux tables, la première table a une ligne et la seconde table a deux lignes. Les tables se rejoignent par la colonne A , et essayez de mettre à jour la colonne B dans le premier tableau. Dans ce cas, il est bon qu'Oracle empêche la mise à jour, sinon la valeur serait non déterministe. Parfois, la valeur était définie sur "1", parfois sur "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;

Le MERGE l'instruction n'a pas les mêmes restrictions. Le MERGE semble essayer de détecter l'ambiguïté au moment de l'exécution, au lieu de la compilation.

Malheureusement MERGE ne fait pas toujours un bon travail de détection de l'ambiguïté. Sur Oracle 12.2, l'instruction ci-dessous fonctionnera occasionnellement, puis échouera. Apporter de petites modifications à la requête peut la faire fonctionner ou échouer, mais je ne trouve pas de modèle spécifique.

--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 échoue au moment de la compilation s'il est théoriquement possible d'avoir des doublons. Quelques déclarations qui devraient le travail ne fonctionnera pas.

MERGE échoue si la base de données détecte des lignes instables au moment de l'exécution. Quelques déclarations qui ne devraient pas le travail sera toujours exécuté.