sql >> Base de Datos >  >> RDS >> Oracle

ORA-01779:no se puede modificar una columna que se asigna a una tabla sin clave conservada

Una cláusula de expresión de tabla DML solo es útil cuando necesita columnas de más de una tabla. En su caso, puede usar una actualización regular 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
    );

Si realmente necesita usar columnas de ambas tablas, tiene tres opciones:

  1. repita la unión en el SET y el WHERE cláusula. Esto es fácil de construir pero no óptimo.
  2. Expresión de tabla DML. Esto debería funciona, si tiene los índices correctos.
  3. MERGE , a continuación se muestra un ejemplo.

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

Esto no responde directamente a su pregunta, sino que proporciona algunas soluciones alternativas. No puedo reproducir el error que estás recibiendo. Necesitaría un caso de prueba completo para investigarlo más a fondo.

Consejos genéricos para vistas actualizables

Uno de los principales problemas de las vistas actualizables es la gran cantidad de restricciones sobre las consultas que pueden contener. La consulta o vista no debe contener muchas funciones, como DISTINCT, GROUP BY, ciertas expresiones, etc. Las consultas con esas funciones pueden generar la excepción "ORA-01732:la operación de manipulación de datos no es legal en esta vista".

La consulta de vista actualizable debe devolver sin ambigüedades cada fila de la tabla modificada solo una vez. La consulta debe ser "clave conservada", lo que significa que Oracle debe poder utilizar una clave principal o una restricción única para garantizar que cada fila se modifique solo una vez.

Para demostrar por qué es importante conservar la clave, el siguiente código crea una instrucción de actualización ambigua. Crea dos tablas, la primera tabla tiene una fila y la segunda tabla tiene dos filas. Las tablas se unen por la columna A e intente actualizar la columna B en la primera tabla. En este caso, es bueno que Oracle evite la actualización, de lo contrario, el valor no sería determinista. A veces, el valor se establece en "1", a veces se establece en "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;

El MERGE declaración no tiene las mismas restricciones. El MERGE La declaración parece intentar detectar la ambigüedad en tiempo de ejecución, en lugar de tiempo de compilación.

Desafortunadamente MERGE no siempre hace un buen trabajo detectando la ambigüedad. En Oracle 12.2, la siguiente declaración funcionará ocasionalmente y luego fallará. Hacer pequeños cambios en la consulta puede hacer que funcione o falle, pero no puedo encontrar un patrón específico.

--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 falla en tiempo de compilación si es teóricamente posible tener duplicados. Algunas declaraciones que deberían el trabajo no se ejecutará.

MERGE falla si la base de datos detecta filas inestables en tiempo de ejecución. Algunas declaraciones que no deberían el trabajo aún se ejecutará.