1

Apex beginner here. I have a view in my Oracle database of the form:

create or replace view vw_awkward_view as
select unique tab1.some_column1,
  tab2.some_column1,
  tab2.some_column2,
  tab2.some_column3
from table_1 tab1,
 table_2 tab2
WHERE ....

I need the 'unique' clause on 'tab1.some_column1' because it has many entries in its underlying table. I also need to include 'tab1.some_column1' in my view because the rest of the data doesn't make much sense without it.

In Apex, I want to create a report on this view with a form for editing it (update only). I do NOT need to edit tab1.some_column1. Only the other columns in the view need to be editable. I can normally achieve this using an 'instead-of' trigger, but this doesn't look possible when the view contains a 'distinct', 'unique' or 'group by' clause.

If I try to update a row on this view I get the following error:

ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.

How can I avoid this error? I want my 'instead-of' trigger to kick in and perform the update and I don't need to edit the column which has the 'unique' clause, so I think it should be possible to do this.

JMc
  • 971
  • 2
  • 17
  • 26
  • 1
    The unique is on all fields- tab1.some_column1,tab2.some_column1, tab2.some_column2, tab2.some_column3. if tab2.some_column1, tab2.some_column2, tab2.some_column3 are already unique then why do you need the unique ? or am I missing something ? – A.B.Cade Feb 09 '12 at 15:54
  • ah..think you've exposed my poor SQL here. I assumed the 'unique' applied only to the column beside where I declared it. This could well be my problem. I'll investigate further thanks... – JMc Feb 09 '12 at 16:18

3 Answers3

2

I think that you should be able to remove the "unique".
if tab2.some_column1, tab2.some_column2, tab2.some_column3 are not unique, then how do you want to update them ?
if they are unique then the whole result: tab1.some_column1, tab2.some_column1, tab2.some_column2, tab2.some_column3 is unique.

When you state in a sql query "unique" or "distinct" it's for all columns not only 'tab1.some_column1'

A.B.Cade
  • 16,735
  • 1
  • 37
  • 53
2

Hope i'm in the correct direction of your question here ;)

Your query could be achieved by doing something like:

select a.some_column1, tab2.some_column1, tab2.some_column2, tab2.some_column3 
  from table_2 tab2 
  join (select distinct some_column1 from table_1) a
    on tab2.column_in_tab1 = a.some_column1

The reason you get the ORA-02014 error is because of the automatically generated ApplyMRU process. This process will attempt to lock a (the) changed row(s):

begin
   for r in (select ... 
               from vw_awkward_view 
              where <your first defined PK column>= 'value for PK1' 
                for update nowait)
   loop
      null;
   end loop;
end;

That's a bummer, and means you won't be able to use the generated process. You'll have to write your own process which does the updating.

For this, you'll have to use the F## arrays in apex_application. If this sounds totally unfamiliar, take a look at: Custom submit process, and on using the apex_application arrays.

Also, here is a how-to for apex from 2004 from Oracle itself. It still uses lots of htmldb references, but the gist of it is there.

(it might be a good idea to use the apex_item interface to build up your form, and have control over what is generated and what array it takes.)

What it comes down to is: loop over the array containing your items and do an UPDATE on your view with the submitted values.

Of course, you don't have locking this way, nor a way to prevent unnecessary updates. Locking you can do yourself, with for example using the select for update method. You'd have to lock the correct rows in the table(s) you want to alter, before you update them. If the locking fails, then your process should fail.

As for the 'lost update' story: here you'd need to check the MD5-checksums. A checksum is generated from the editable columns in your form and put in the html-code. On submit, this checksum is then compared to a newly generated checksum from those same columns, but with values from the database at that time of submit. If the checksums differ, it means the record has changed between the page load and the page submit. Your process should fail because the record has been altered, and you don't want to have those overwritten. (if you go the apex_item way, then don't forget to include an MD5_CHECKSUM call (or MD5_HIDDEN).

Important note though: checksums generated by either using apex_item or simply the standard form functionality build up a string to be hashed. As you can see in apex_item.md5_hidden, checksums are generated using DBMS_OBFUSCATION_TOOLKIT.MD5. You can get the checksum of the values in the DB in 2 ways: wwv_flow_item.md5 or using dbms_obfuscation. However, what the documentation fails to mention is this: OTN Apex discussion on MD5 checksums. Pipes are added in the generated checksums! Don't forget this, or it'll blow up in your face and you'll be left wondering for days what the hell is wrong with it.

Example:

select utl_raw.cast_to_raw(dbms_obfuscation_toolkit.md5(input_string=>
         "COLUMN1"    ||'|'||
         "COLUMN2"    ||'|'||
         "COLUMN5"    ||'|'||
         "COLUMN7"    ||'|'||
         "COLUMN10"   ||'|'||
         "COLUMN12"   ||'|'||
         "COLUMN14"   ||
         '|||||||||||||||||||||||||||||||||||||||||||'
      )) md5
from some_table

To get the checksum of a row of the some_table table, where columns 1,2,5,7,10,12,14 are editable!

In the end, this is how it should be structured:

  1. loop over array
  2. generate a checksum for the current value of the editable columns from the database
  3. compare this checksum with the submitted checksum (apex_application.g_fcs if generated) if the checksums match, proceed with update. If not, fail process here.
  4. lock the correct records for updating. Specify nowait, and it locking fails, fail the process
  5. update your view with the submitted values. Your instead-of trigger will fire. Be sure you use correct values for your update statement so that only this one record will be updated

Don't commit inbetween. It's either all or nothing.

I almost feel like i went overboard, and it might feel like it is all a bit much, but when you know the pitfalls it's actually not so hard to pull this custom process off! It was very knowledgable for me to play with it :p

Community
  • 1
  • 1
Tom
  • 6,988
  • 1
  • 26
  • 40
0

The answer by Tom is a correct way of dealing with ths issue but I think overkill for your requirements if I understand correctly.

The easiest way may be to create a form on the table you want to edit. Then have the report edit link take the user to this form which will only update the needed columns from the one table. If you need the value of the column from the other table displayed it is simple when you create the link to pass this value to the form which can contain a display only item to show this.

Emu
  • 494
  • 6
  • 15