helrich
helrich

Reputation: 1310

Updating a Join View without getting Non Key-Preserved Error

I have a view created in an Oracle 11g database that is comprised of two joined tables like so:

CREATE FORCE VIEW my_dataview
(
   key1,
   key2,
   column_from_table1,
   column_from_table2
)
AS
   SELECT key1,
          key2,
          column_from_table1,
          column_from_table2
     FROM table1
          NATURAL LEFT OUTER JOIN table2;

where both source tables have two primary keys named key1 and key2. The data is being aggregated correctly, but I cannot update any of the columns from table2 directly from the view.

If I were to execute the following update,

UPDATE my_dataview SET column_from_table2 = 'Hello, world' 
WHERE key1 = 1234
     AND key2 = 12;

the error it gives is ORA-01779 cannot modify a column which maps to a non key-preserved table. It also highlights column_from_table2. If I were to execute the same command, but set column_from_table1 instead, it works. I think that may be because table1 is the required table in the join.

I first thought that maybe since I'm joining on the primary keys, and there is only one resulting column in the view, that would explain my problem. But even adding in the separate table2.key1 and table2.key2 columns to the view did not change anything, except replicating data unnecessarily.

I know it is possible to update a view using an INSTEAD OF trigger, but I would prefer to keep this as clean I can -- that seems more like a hack than a solution anyways.

So to summarize my question, what is the best plan of action for enabling insert, update, and delete on a join view so as to not even be able to tell it is a view comprised of two separate tables at all?

Table definitions as requested:

CREATE TABLE table1
(
   key1                 NUMBER(5)    NOT NULL,
   key2                 NUMBER(2)    NOT NULL,
   column_from_table_1  DATE  
);

CREATE UNIQUE INDEX pk_table1_index ON table1
(key1, key2);

ALTER TABLE table1 ADD (
   CONSTRAINT table1_Pkey
   PRIMARY KEY
   (key1, key2)
   USING INDEX pk_table1_index
   ENABLE VALIDATE);

CREATE TABLE table2
(
   key1                 NUMBER(5)    NOT NULL,
   key2                 NUMBER(2)    NOT NULL,
   column_from_table_2  VARCHAR2(20)
);

CREATE UNIQUE INDEX pk_table2_index ON table2
(key1, key2);

ALTER TABLE table2 ADD (
   CONSTRAINT table2_Pkey
   PRIMARY KEY
   (key1, key2)
   USING INDEX pk_table2_index
   ENABLE VALIDATE);

Upvotes: 3

Views: 4458

Answers (2)

krokodilko
krokodilko

Reputation: 36107

You cannot update column_from_table2 in this view, it's not updatable.
To check whether some column is updatable or not, run this query:

SELECT * FROM USER_UPDATABLE_COLUMNS
WHERE table_name = 'MY_DATAVIEW';

OWNER                          TABLE_NAME                     COLUMN_NAME                    UPDATABLE INSERTABLE DELETABLE
------------------------------ ------------------------------ ------------------------------ --------- ---------- ---------
TEST                           MY_DATAVIEW                    KEY1                           YES       YES        YES       
TEST                           MY_DATAVIEW                    KEY2                           YES       YES        YES       
TEST                           MY_DATAVIEW                    COLUMN_FROM_TABLE1             YES       YES        YES       
TEST                           MY_DATAVIEW                    COLUMN_FROM_TABLE2             NO        NO         NO   

Why it is not updatable ? It's a big question.
There is a whole chapter in documentation on this topic:
http://docs.oracle.com/cd/B28359_01/server.111/b28310/views001.htm#i1006234
find "Updating Views That Involve Outer Joins", there is an example of a view with outer join and detailed explanation which columns in this view can and cannot be updated and why.

Upvotes: 3

Robert
Robert

Reputation: 25753

Try to change your view to this:

CREATE FORCE VIEW my_dataview
(
   key1,
   key2,
   column_from_table1,
   column_from_table2
)
AS
   SELECT t1.key1,
          t1.key2,
          t1.column_from_table1,
          t2.column_from_table2
     FROM table1 t1
     LEFT OUTER JOIN table2 t2 on t1.key1 = t2.key1
                               and  t1.key2 = t2.key2;

Upvotes: 0

Related Questions