how to display single record in tabular form? - oracle

I want to display single record one by one when button press in tabular form
how to do this in oracle forms?

I'd suggest you to use Forms built-in capabilities and spend time and energy on something else.
i want to display single record when press button single forward and all records when press button double forward
I presume that those buttons ("single" and "double forward") are custom-made buttons (i.e. you created them).
If so, "Double forward" is simple - put EXECUTE_QUERY into its WHEN-BUTTON-PRESSED trigger (assuming that this is a data block, based on a table).
"Single forward" isn't that simple as you can't execute query, so you have to write some code. For example, you could "prepare" result data set into another table, specifying the row number so that you could later use it:
insert into temp_table (rn, deptno, dname, loc)
select rownum, deptno, dname, loc
from dept
where ...
WHERE clause is kind of tricky because user can enter search criteria. How to know it? Use GET_BLOCK_PROPERTY. As it brings scent of dynamic SQL, you'd have to compose the INSERT statement - consider creating a stored procedure which utilizes EXECUTE IMMEDIATE (or, alternatively, see if FORMS_DDL can be used).
As many users can use the same form simultaneously, that "temp table" should be a global temporary table or - if not - you'd have to save user information as well; otherwise, you'll create a chaos.
Suppose data is now prepared. In a form, you'd have to create a parameter (or a global variable) which holds current "row number" (rn) value. "Single forward" button's trigger would then
last_record;
create_record;
select deptno, dname, loc
into :blk.deptno, :blk.dname, :blk.loc
from temp_table where rn = :parameter.rn;
-- prepare the next row
:parameter.rn := :parameter.rn + 1;
OK, now you have your data, and new problems arrive. As it is a data block, inserting values into it in that manner presumes that you inserted brand new set of information, and Forms will treat it like that. So, if you try to save it, you'll get a unique key violation (which is OK) or duplicate data (which is bad).
It means that "data block" should, actually, be a "control block", which isn't related to a table. Furthermore, it means that "Double forward" can't work the way I described, but by modifying "Single forward" button's code (don't fetch row-by-row, but all of them at once).
If it is a control block, now you have to find a way to store modified values as well as newly added records, so you'd have to create your own ON-INSERT, ON-UPDATE, etc. triggers.
Shortly, I don't think that what you want is a simple task. Did I already say that you shouldn't do it in the first place? Well, I still think so.
Hopefully, someone else will have a better, simpler suggestion.

Related

i have two table with foreign key and i want to press in foreign key in first table and the second table appear data

I have two blocks. First one is wizard and second is manual in Oracle Forms 11g with relational.
I execute the data into first block and I want to press in foreign key in first table and the second table appear data which is link together by the same number foreign key?
Which trigger should I use please? And which code should I put into trigger?
You can use WHEN-NEW-RECORD-INSTANCE trigger at block level of Block1 with code :
declare
v_skulist table1.skulist%type;
begin
v_skulist := :Block1.f_skulist; --> represents left uppermost field
go_block('Block2');
execute_query;
go_block('Block1'); --> go back to the upper block again, if don't want to come back, then starting from this line upto `end;`(exclusive) should be removed.
while v_skulist != :Block1.f_skulist
loop
next_record;
end loop;
end;
where
Query Data Source Name property is set to table1 for Block1
and
Query Data Source Name property is set to myuser1.table2 with
WHERE Clause set to
skulist = :Block1.skulist for Block2
assuming the second table is on the other user at least
with granted select privilege to your current user as mentioned in your comment.
This way, whatever record touched in the first block, the counterpart foreign key column is brought in the second block.
You should have used the data block wizard which creates all necessary triggers and procedures that take care about it.
If you're going to do it manually, well ... you probably shouldn't do that, there are too many procedures involved.
I suggest you remove detail block and create it from scratch, this time using the wizard. Pay attention to what it asks and - once you're done - the form will automatically do what you want it to.
Then, you can review what objects Forms created for you and - if you really REALLY want to do it manually, try to mimic its steps.

PL/SQL: ORA 01422 fetch returns more than requested number of rows

I am developing an order transaction where a user can order a product. Once they clicked the 'add to cart' button, it will be able to save on the database in how many times they want with the same order id. Order id is like a transaction id.
My problem is that whenever I want to display the items that customer ordered, it displays an error or ORA 01422. How can I resolve this error?
Here is my code
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
SELECT MOTOR_ID,
MOTOR_QTY_PURCHASED,
UNITPRICE
INTO :MOTOR_ORDER_ITEMS.MOTOR_ID,
:MOTOR_ORDER_ITEMS.MOTOR_QTY_PURCHASED,
:MOTOR_ORDER_ITEMS.UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id;
END;
As krokodilo says, this error is caused because your query returns multiple rows. Depending on what you want to do, you have a couple of options.
If you want multiple values then either use a loop and process them one row at a time (if you are going to be performing a dml operation use bulk collect). If you only want a single row then narrow your result set down with an extra where clause, or use MAX to ensure you only get one value back.
If there is more than one row which will be returned from a query you'll need to use a cursor. One way to do this is with a cursor FOR loop:
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
FOR aRow IN (SELECT MOTOR_ID, MOTOR_QTY_PURCHASED, UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id)
LOOP
-- Do something here with the values in 'aRow'. For example, you
-- might print them out:
DBMS_OUTPUT.PUT_LINE('MOTOR_ID=' || aRow.MOTOR_ID ||
' MOTOR_QTY_PURCHASED=' || aRow.MOTOR_QTY_PURCHASED ||
' UNITPRICE=' || aRow.UNITPRICE);
END LOOP;
END;
Best of luck.
This looks like a Forms question; is it? If so, my suggestion is to let Forms do that job for you.
According to what you posted, there are two blocks:
MOTOR_PRODUCTS_ORDER (a master block, form type)
MOTOR_ORDER_ITEMS (a detail block, tabular type)
I guess that there is a master-detail relationship between them. If there's none, I'd suggest you to create it. Although you can make it work without such a relationship, it'll be much more difficult. If you are unsure of how to do it, start from scratch:
delete MOTOR_ORDER_ITEMS block (detail)
create it once again, this time by following the Data Block Wizard
Set MOTOR_PRODUCTS_ORDER to be its master block
Relationship is on ORDER_ID column/item
Let's presume that by this point everything is set up. Retrieving items that belong to that ORDER_ID is now very simple:
navigate to master block
enter query mode
enter value into an item that represents ORDER_ID
execute query
End of story. Forms triggers & procedures (which were created by the Wizard) will do its job and retrieve both master and detail records.
No need for additional coding; if you're skilled developer, you can create such a form in a matter of minutes. Won't be beautiful, but will be effective & reliable.
There's really no use in doing it manually, although it is possible. Your code works if there's a single item for that ORDER_ID. For two or more items, as you already know, it'll fail with TOO-MANY-ROWS error.
Basically, if you insist, you should use a loop:
you'd enter ORDER_ID into the master block
as you need to move through the detail block, i.e. use NEXT_RECORD, which is a restricted procedure, you can't use number of triggers (open Forms Online Help System and read about them) so a "Show items" button (with its WHEN-BUTTON-PRESSED trigger) might be just fine
a cursor FOR loop would be your choice
for every row it fetches, you'd populate block items and
navigate to next record (otherwise, you'd keep overwriting existing values in the 1st tabular block row)
As I said: possible, but not recommended.

Oracle error: ORA-04079: invalid trigger specification

I am new to Oracle and would like to know how to make this trigger work please. I can do each trigger separately but I need them all in the same one if that makes sense.
create trigger ID_trigger
before insert on crime, evidence, offence, officer
for each row
begin
select crime_seq.nextval into :new.crime_id from dual
and officer_seq.nextval into :new.officer_id from dual
and evidence_seq.nextval into :new.evidence_id from dual
and offence_seq.nextval into :new.offence_id from dual;
end;
I initially had a single trigger for each table. However when submitting data into my form the triggers seemed to have overwritten the previous one
" I initially had a singler trigger for each table, however when
sumbitting data into my form the triggers seemed to have overwritten
the previous one"
Let's guess: you called all four triggers id_trigger. Each subsequent CREATE OR REPLACE TRIGGER call would overwrite the first one. Unless you used CREATE TRIGGER as you do here, in which case each subsequent call would fail, Either way, only one table would have a trigger.
You see, even though they belong to a table, triggers are separate database objects. So, like indexes or constraints, their names must be unique within the schema.
The solution is simple: give each trigger a different name, say by including the table name.

Oracle: Monitoring changes in v_$parameter

Long time user, first time "asker".
I am attempt to construct an Oracle procedure and/or trigger that will compare two tables with the MINUS operation and then insert any resulting rows into another table. I understand how to do the query in standard SQL, but I am having trouble coming up with an efficient way to do this using PL/SQL.
Admittedly, I am very new to Oracle and pretty green with SQL in general. This may be a silly way to go about accomplishing my goal, so allow me to explain what I am attempting to do.
I need to create some sort of alert that will be triggered when the V_$PARAMETER view is changed. Apparently triggers can not respond to changes to view but, instead, can only replace actions on views...which I do not wish to do. So, what I did was create a table that to mirror that view to essentially save it as a "snapshot".
create table mirror_v_$parameter as select * from v_$parameter;
Then, I attempted to make a procedure that would minus these two so that, whenever a change is made to v_$parameter, it will return the difference between the snapshot, mirror_v_$parameter. I trying to create a cursor with the command:
select * from v_$parameter minus select * from mirror_v_$parameter;
to be used inside a procedure, so that it could be used to fetch any returned rows and insert them into another table called alerts_v_$parameter. The intent being that, when something is added to the "alert" table, a trigger can be used to somehow (haven't gotten this far yet) notify my team that there has been a change to the v_$parameter table, and that they can refer to alerts_v_$parameter to see what has been change. I would use some kind of script to run this procedure at a regular interval. And maybe, some day down the line when I understand all this better, manipulate what goes into the alerts_v_$parameter table so that it provides better information such as specifically what column was changed, what was its previous value, etc.
Any advice or pointers?
Thank you for taking the time to read this. Any thoughts will be very appreciated.
I would create a table based on the exact structure of v_$parameter with an additional timestamp column for "last_update", and periodically (via DBMS_Scheduler) merge into it any changes from the real v_$parameter table and capture the timestamp of any detected change.
You might also populate a history table at the same time, either using triggers on update of your table or with SQL.
PL/SQL is unlikely to be required, except as a procedural wrapper to the SQL code.
Examples of Merge are in the documentation here: http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_9016.htm#SQLRF01606

Oracle Apex - Updating a view with instead-of trigger

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.
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'
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:
loop over array
generate a checksum for the current value of the editable columns
from the database
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.
lock the correct records for updating. Specify nowait, and it
locking fails, fail the process
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
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.

Resources