Oracle automatically insert record in multirecord block part 2 - oracle

My table looks like this:
+-------------------+
|Name |
+-------------------+
|Name1 |
|Name2 |
|Name3 |
|Name4 |
|Name1Jr |
|Name2Jr |
|Name4Jr |
+-------------------+
My multirow block looks like:
What I wanted to know is how can I insert a record that has the same name with Jr after I insert a name. For example, I inserted Name2, it will also insert Name2Jr into the multirow block. Like this:
Note: I need to get the value of the automatically inserted data from database.
I tried in WHEN-NEW-RECORD-INSTANCE trigger(the answer of Sir #Littlefoot on my last question):
if :system.trigger_record = 1 and :test.name is null then
-- do nothing if it is the first record in a form
null;
else
duplicate_record;
if substr(:test.name, -2) = 'Jr' then
-- you've duplicated a record which already has 'Jr' at the end - don't do it
:test.name := null;
else
-- concatenate 'Jr' to the duplicated record
:test.name := :test.name || 'Jr';
end if;
end if;
And now, what I want is to know if there is a way to do it on WHEN-VALIDATE-RECORD trigger. The problem there is that the duplicate_record can't be used in WHEN-VALIDATE-RECORD trigger. How to do it using procedure, function or something? Thanks in advance!

DUPLICATE_RECORD is a restricted procedure and you can't use it in WHEN-VALIDATE-RECORD trigger (or any other of the same kind).
As you have to navigate to the next record (if you want to copy it), even if you put that restricted procedure into another PL/SQL program unit, everything will just propagate and - ultimately - raise the same error. So ... you're out of luck.
Even if you wrote a (stored) procedure which would insert that "Jr" row into the database somewhere behind the scene, you'd have to fetch those values to the screen. As EXECUTE_QUERY is the way to do it, and as it is (yet another) restricted procedure, that won't work either.
If you planned to clear data block and fill it manually (by using a loop), you'd have to navigate to next (and next, and next) record with NEXT_RECORD, and that's again a restricted procedure. Besides, if it was a data block (and yes, it is), you'd actually create duplicates for all records once you'd save changes so - either it would fail with unique constraint violation (which is good), or you'd create duplicates (which is worse).
BTW what's wrong with WHEN-NEW-RECORD-INSTANCE? What problems do you have when using it?

What We need is
a Data Block (BLOCK1) with
Query Data Source Name is mytable,
and Number of Records Displayed set to more than 1(let it be 5 as in your case).
a Text item named as NAME same as the column name of the table
mytable, and Database Item property set to Yes.
a Push Button with Number of Records Displayed set to 1,
Keyboard Navigable and Mouse Navigate properties are set to No
and has the following code (inside WHEN-BUTTON-PRESSED trigger):
commit;
Query_Block1;
Where Query_Block1 is beneath Program Units node with this code :
PROCEDURE Query_Block1 IS
BEGIN
execute_query;
last_record;
down;
END;
A POST-INSERT trigger beneath BLOCK1 node with code :
insert into mytable(name) values(:name||'Jr');
An ON-MESSAGE trigger at the form level with the code (to suppress the messages after commit):
if Message_Code in (40400, 40401) then
null;
end if;
And WHEN-NEW-FORM-INSTANCE trigger with the code :
Query_Block1;

Related

Browse the values of multiple fields and insert them into the same column

I am trying to create a functionality for my oracle apex application using duplicated row. Let's say that i have a vehicle table.
CREATE TABLE vehicles
(
brand VARCHAR2(50),
model VARCHAR2(50),
comment VARCHAR2(50)
);
I first created a PL/SQL procedure that is currently working, which duplicate the row n amount of times, selected by the user. For example, if the user has 5 of the same vehicles, he can enter "5", and the application will insert 5 times the same vehicle, with different primary keys of course.
What I am trying to achieve, is to give the possiblity for the user to add a custom comment on every row. For example, he would have 5 of the same cars, with different comment on each one.
Here's the code of my procedure when the user is inserting the cars :
FOR i IN 1..:P27_NUMBER_VEHICLES -- this represents the numbers of cars the users want to insert
LOOP
INSERT INTO vehicles(brand, model)
VALUES(:P27_BRAND, :P27_MODEL);
END LOOP;
END;
This creates 5 of the same vehicles, but i want the user to be able to add 5 different comments on each vehicle, here's an example of my insertion form, the comment field appears dynamically based on the numbers that the user want to insert
I've now tried to update my procedure with the comment, i tried to give a name that could be dynamically changed by the procedure, I named it :P27_COMMENT_0 / 1 / 2 / 3 / 4 / 5. So i tried to add :P27_COMMENT_ || i (the index of the loop) to try to browse the differents fields automatically, here's the code :
BEGIN
FOR i IN 1..:P27_NUMBER_VEHICLES
LOOP
INSERT INTO vehicles(brand, model, comment)
VALUES(:P27_BRAND, :P27_MODEL, :P27_COMM_ || i);
END LOOP;
END;
This unfortunately doesn't work.
I hope you guys could have a solution for me. To summarize everything, I am trying to duplicate a row witha loop, but still being able to add a custom entry for each one on a specific column.
Do not hesitate to ask for more details, as I may have forgot some important things.
Thank you in advance,
Thomas
UPDATE
Here's how it is showing, I can't write anything on it :
In my opinion is easier to use APEX_ITEM, as follow:
Add an Interactive Report region or sub region after your items.
Set the IR SQL query as:
SELECT
APEX_ITEM.TEXT(
p_idx => 1,
p_attributes=> 'placeholder="Comment '|| LEVEL ||'"') AS Name
FROM dual
CONNECT BY LEVEL <= TO_NUMBER (:P16_NUM)
;
:P16_NUM is an item which holds the number of items to display. Do not forget to add it in Page Items to Submit in the Source section of the IR.
Add a submit button.
In Processing create a new process an iterate through the items as:
BEGIN
apex_debug.enable;
for i in apex_application.g_f01.FIRST..apex_application.g_f01.LAST loop
apex_debug.info('####### Comment '||i||' '||apex_application.g_f01(i));
end loop;
END;
Finally your INSERT:
INSERT INTO vehicles(brand, model, comment)
VALUES(:P27_BRAND, :P27_MODEL, apex_application.g_f01(i));
When you check that everything works fine, change the process code:
BEGIN
for i in apex_application.g_f01.FIRST..apex_application.g_f01.LAST loop
INSERT INTO vehicles(brand, model, comment)
VALUES(:P27_BRAND, :P27_MODEL, apex_application.g_f01(i));
end loop;
END;
EDIT
Select the column where the inputs are, on the right find the Security section and disable Escape special characters.

Update table using multi record block in post forms commit

what I wanted is to update my table using values from a multi record block and here is what I tried in post forms commit:
BEGIN
FIRST_RECORD;
LOOP
UPDATE table1
SET ord_no = :blk.new_val;
EXIT WHEN :SYSTEM.LAST_RECORD='TRUE';
NEXT_RECORD;
END LOOP;
END;
but when I save I got an error
FRM-40737: Illegal restricted procedure
FIRST-RECORD in POST-FORMS-COMMIT trigger.
OK, a few things to talk about here
1) I'm assuming that 'table1' is NOT the table on which the block is based. If the block was based on table1, simply as the user edits entries on screen, then when you do a COMMIT_FORM command (or the users clicks Save) then the appropriate updates will be done for you automatically. (That is the main use of a database-table based block).
2) So I'm assuming that 'table1' is something OTHER than the block based table. The next thing is that you probably need a WHERE clause on your update statement. I assume that you are updating a particular in table1 based on a particular value from the block? So it would be something like:
update table1
set ord_no = :blk.new_val
where keycol = :blk.some_key_val
3) You cannot perform certain navigation style operations when in the middle of a commit, hence the error. A workaround for this is to defer the operation until the completion of the navigation via a timer. So your code is something like:
Declare
l_timer timer;
Begin
l_timer := find_timer('DEFERRED_CHANGES');
if not id_null(l_timer) then
Delete_Timer(l_timer);
end if;
l_timer := Create_Timer('DEFERRED_CHANGES', 100, no_Repeat);
End;
That creates a timer that will fire once 100ms after your trigger completes (choose name, and time accordingly) and then you have your original code on a when-time-expired trigger.
But please - check out my point (1) first.

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 APEX Selection list

I have a form on a table with some values (text fields) and a select list. The select list is declared in shared components and shows me values from another table. Also I have some process (after submit) to modify and create new table entry. Everything works fine without propagating values from select list. There are no errors from the process. It looks like the process didn't get a value for :P22_WORKER_LIST from the selection list. It should work when I push the create or save buttons but nothing happened. Every instruction in BEGIN END block runs great without this one.
Process:
BEGIN
<some instructions>
UPDATE "WORKER" SET ACCOUNT_LOGIN = :P22_LOGIN
WHERE SURNAME = :P22_WORKER_LIST;
END;
Thank you for suggestion with the session state. After submiting page it turned out that the value for my :P22_WORKER_LIST is a WORKER_ID instead a SURNAME.
BEGIN
<some instructions>
UPDATE "WORKER" SET ACCOUNT_LOGIN = :P22_LOGIN
WHERE WORKER_ID = :P22_WORKER_LIST;
END;

Oracle PL/SQL script generation from excel sheet

I'm having requirement to generate a insert script file from excel sheet. I'm partly successfully in generating script file. But i got struck in a situation,I need help from any1.
My logic is some thing like this, Read first cell,check if the value in the cell already exists in DB.If not, generate an insert script as follow
declare
PK1 integer;
begin
select tablename_seq.currval into PK1 from dual;
insert into TableName valuestablename_seq_seq.nextval,'Blagh',1);
end;
Im storing PK1 in hashtable with data has KEY .so that if the same data appears in the next rows,using Hashtable search, I will get the hashtable value for corresponding data key and pass it has parameter to another insert script. But every time i generate new variable like PK1,Pk2...etc.I have keep 'BEGIN' key word after Declare and also add 'END' key word after every insert,If i do so scope of variable goes out off scope.I may be using those declared variables in another insert statements has a parameter. Is there any chance of saving PK1,Pk2..... has session/Global variables for the script execution. So they wil become avialable for entire script execution time.
My inclination is to say that each line of your spreadsheet should just be creating a statement like insert into TableName values (tablename_seq_seq.nextval,'Blagh',1) returning ID into PK1;, then wrap the whole thing in a single DECLARE-BEGIN-END block with the appropriate variables defined, something like:
declare
pk1 integer;
pk2 integer;
begin
insert into TableName
values (tablename_seq_seq.nextval,'Blagh',1)
returning ID into PK1;
insert into TableName
values (tablename_seq_seq.nextval,'Urgh',2)
returning ID into PK2;
[...]
end;
You could even create the list of variable declarations in one column and the SQL in another, then copy and paste them into the right place in the block.
I'd start off with a
DECLARE
PROCEDURE action (p_val IN INTEGER) IS
...
END action;
BEGIN
Then have each line in the spreadsheet just do a call to the procedure so that a spreadsheet entry of 1 becomes
action (1);
Then you end up with something like
DECLARE
PROCEDURE action (p_val IN INTEGER) IS
...
END action;
BEGIN
action (1);
action (8);
action (23);
action (1);
action (1);
END;
The action procedure can be as complicated as you like, storing information in tables/arrays whatever.
Is the question about the best way to update a database from a spreadsheet, or the best way to generate a script from a spreadsheet?
I would recommend loading the spreadsheet data into a temporary table and then using a simple INSERT/SELECT statement, unless you're worried about uniqueness collisions in which case I would use a MERGE statement instead. This is much easier than trying to generate a script with logic for each insert statement.
Instead of each Line, Consider as each cell. Each cell will generate insert script into corresponding cell. I have created in the same way, problem is If i want to use PK1 variable some where in the row 10 ,column 10 (cell value) insert script,because we are ending 'end' immediately after Begin block, the scope of PK1 always be remain in Begin block.For this i have created Begin with one insert and then create another Begin with another insert and so on.and # the end im adding end;end;But the problem with above method is,I'm trrying to insert 200 row X 200 columns = 400 cells insert scripts. in this flow when i try to run script, it throws an runtime error ' Stack Overflow'
If you use Oracle E-Business, you might be interested by webadi.
This tool creates a excel files to be populated and then loaded into database via a procedure. You can then validate your data.
Creating custom Web ADI Integrators
WebADI - Using a Custom Integrator

Resources