I have a procedure with tasks in it.
I have to do something after the all of the tasks terminated.
How can I do that?
Declare the tasks in an inner block: the block won't exit until all the tasks are complete, ARM7.6.1(4)
with Ada.Text_IO; use Ada.Text_IO;
procedure After_Tasks is
begin
Put_Line ("at the start");
declare
task T1;
task T2;
task body T1 is
begin
delay 1.0;
Put_Line ("t1 done");
end T1;
task body T2 is
begin
delay 2.0;
Put_Line ("t2 done");
end T2;
begin
null;
end; -- block here until T1 & T2 are completed
Put_Line ("at the end");
end After_Tasks;
Without any knowledge of what you're actually trying to accomplish, a couple stabs at accomplishing this would be:
Monitor (poll) each pending task's 'Terminated attribute.
Implement a "Shutdown" entry in your task(s) that is the last thing each task performs. Have your "controller" rendezvous with each task's Shutdown entry and once all tasks have accepted and completed the rendezvous, for all intents and purposes you can conclude that the tasks have all terminated. For the pedantic among us, we might execute a short delay (delay 0.0;) and then verify via the 'Terminated attribute that all tasks are terminated, or at least pragma Assert() so.
Related
I have a code block that takes some time to finish and I've created a progress variable on a table that the process will update so the end user can know how much is left to complete.
The thing is that the code block is treated as an atomic transaction so the progress variable value will only show 0% or 100%, unless I use the commit statement, which will remove a savepoint declared at a previous point on the block, and if an exception occurs it wont be recognized as valid.
The code is something like this:
begin
/*do some stuff*/
savepoint p_savepoint;
for q in (somequery) loop /*Really long loop*/
/*Do some other stuff*/
update t_sys_state set p1_progress = percentage
where user_id = 'theuserid';
commit; /*This commit make the real progress value available*/
end loop;
exception
when others then
rollback to p_savepoint; /*This savepoint is not recognized because of the previous commit*/
raise;
end;
Is there any way around this?
It sounds like you want to update your progress in an autonomous transaction. This is one of very, very few situations where using an autonomous transaction makes sense.
CREATE OR REPLACE PROCEDURE log_progress( p_user IN varchar2,
p_percentage IN number )
AS
PRAGMA autonomous_transaction;
BEGIN
UPDATE t_sys_state
SET p1_progress = p_percentage
WHERE user_id = p_user;
commit;
END;
and then
begin
/*do some stuff*/
savepoint p_savepoint;
for q in (somequery) loop /*Really long loop*/
/*Do some other stuff*/
log_progress( 'theuserid', percentage );
end loop;
exception
when others then
rollback to p_savepoint; /*This savepoint is not recognized because of the previous commit*/
raise;
end;
im new with PL_SQL, i want to ask a question. A need to terminate this script when reaches te exit condition, but its failing, it continues. Im not able to terminate the execution of the script when WHENEVER SQLERROR EXIT SQL.SQLCODE is reached. Any suggestions??
WHILE V_COUNT_MENSAJES>0
LOOP
DELETE
FROM TB_ICP_ENVIOMENSAJES
WHERE KEY_MENSAJE = P_KEY_MENSAJE;
WHENEVER SQLERROR EXIT SQL.SQLCODE;
END LOOP;
END;
WHEN others THEN
dbms_output.put_line('Error en la consulta!');
END;
-----REST OF CODE
Try to incorporate the below snippet it will help to terminate the
code whenever you reach any exception. Hope this helps.
DECLARE
p_err_cd PLS_INTEGER;
p_err_msg VARCHAR2(32767 CHAR);
V_COUNT_MENSAJES PLS_INTEGER;
P_KEY_MENSAJE VARCHAR2(1000 CHAR);
BEGIN
WHILE V_COUNT_MENSAJES > 0
LOOP
BEGIN
DELETE FROM TB_ICP_ENVIOMENSAJES WHERE KEY_MENSAJE = P_KEY_MENSAJE;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,SQLERRM,TRUE);
END;
END LOOP;
END;
You don't have to use "WHENEVER SQLERROR EXIT SQL.SQLCODE;" code inside PLSQL block.Whenever error occur control will pass to exception.Please see the below example for the same.
DECLARE
V_COUNT_MENSAJES NUMBER:=10;
ERROR_1 EXCEPTION;
BEGIN
WHILE V_COUNT_MENSAJES>0
LOOP
RAISE ERROR_1;
END LOOP;
EXCEPTION
WHEN others THEN
dbms_output.put_line('Error en la consulta!');
END;
/
Also please note when you’re executing a script, there are many cases in which you want an error to cause SQL*Plus to terminate where we use the following command and not inside plsql block:
SQL> WHENEVER SQLERROR EXIT SQL.SQLCODE
I had two buttons that do different job but there is check data entry in common
so I made a program unit for that check then I call it from these two buttons
but my problem is that when there is an error within the check I got the message for user and all things I made but the its get back to the code within button and continue progressing I set a return keywords at end of each condition to sop the code from running but its not working please whats the problem ? how I can stop my code until the error check passed ?!!
example of check data entry program unit
PROCEDURE CHECK_ENTRY IS
BEGIN
IF :block1.text_item1 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block1.text_item1',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg .');
RETURN;
ELSIF :block2.text_item2 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block2.text_item2',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg2 .');
RETURN;
END IF ;
END;
example of code within on_button_press trigger
PROCEDURE procedure_name IS
BEGIN
IF FORM_SUCCESS THEN
DISPLAY_ERROR;
:block1.text_item1:= :block2.text_item2;
:block2.text_item2:=:block1.text_item1;
**CHECK_ENTRY;** /* here is the calling of previous program unit that check the data entry then get back to here in case there is no error and continue commit the data and disabling text item so user wont be able to modify the data */
COMMIT_FORM;
program_unit('ORDER_DONE');
ELSE
ROLLBACK;
DISPLAY_ERROR;
SHOW_MESSAGE('please connect administrator.');
END IF;
END;
Your code should be like this:
PROCEDURE CHECK_ENTRY IS
BEGIN
IF :block1.text_item1 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block1.text_item1',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg .');
raise form_trigger_failure;
ELSIF :block2.text_item2 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block2.text_item2',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg2 .');
raise form_trigger_failure;
END IF ;
END;
PROCEDURE procedure_name IS
BEGIN
:block1.text_item1:= :block2.text_item2;
:block2.text_item2:=:block1.text_item1;
CHECK_ENTRY;
IF FORM_SUCCESS THEN
DISPLAY_ERROR;
COMMIT_FORM;
program_unit('ORDER_DONE');
ELSE
ROLLBACK;
DISPLAY_ERROR;
SHOW_MESSAGE('please connect administrator.');
END IF;
END;
I have a pl sql code that execute three queries sequentially to determine a match level and do some logic
The issue is - when first query has no results (completely valid scenario) I get ORA-01403 No data found.
I understand that I need to incorporate [ Exception clause when NO_DATA_FOUND ]- but how to add it and continue to the next query?
PL/SQL Code
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =2
if A is null then
do some logic 2;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =3
if A is null then
do some logic 3;
end if
END PL/SQL Code
Declare
--your declarations
begin
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
Begin
MATCH_LEVEL =1;
if A is null then
do some logic;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('Error...');
END;
--- and son on for other blocks
end;
Just surround your SELECT INTO with begin-end;
begin
-- your faulty statement here
Exception
When NO_DATA_FOUND Then
-- Do what you want or nothing
WHEN TOO_MANY_ROWS THEN
-- what if you get more then one row? and need specific handler for this
When OTHERS Then
-- do something here or nothing (optional - may happen if you have more than your SELECT INTO between 'begin' and 'Exception')
end;
This is like try block of PL/Sql
With this technique you can log the reason your statement failed.
For a SELECT ... INTO ... statement, the PL/SQL engine assume there will be one, and only one row returned by your query. If there is no row, or more than one, an exception is raised.
FWIW, you can handle such cases without resorting on exception handling by using aggregate functions. That way, there will always be only one row in the result set.
Assuming A can't be NULL in your rows:
SELECT MAX(A) into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- A would be NULL if there was *no* row. Otherwise, it is *the* value for *the* row
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
If the NULL value is a possible case, just add an extra COUNT(*) column:
SELECT MAX(A), COUNT(*) into A, HAS_FOUND_ROW FROM SAMPLE WHERE SOME CONDITION;
if HAS_FOUND_ROW > 0 then
...
end if;
Oracle will not allow you to open an implicit cursor (i.e. a select statement in the body of a code block) that returns no rows. You have two options here (3 really, counting #Sylvain's answer, but that is an unusual approach): use an explicit cursor or handle the error.
Explicit Cursor
An explicit cursor is one found in the DECLARE section it must be opened and fetched manually (or in a FOR loop). This has the added advantage that, if you parameterize the query properly, you can write it once and use it multiple times.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
cursor cur_params (some_column_value number) is
SELECT A FROM SAMPLE WHERE some_column = some_column_value;
BEGIN
MATCH_LEVEL := 1;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
end;
Handle the error
If you choose to handle the error, you'll need to create a BEGIN...END block around the code that is going to throw the error. When disregarding an error, it's crucial that you ensure that you are only disregarding the specific error you want avoid, when generated from the specific statement you expect it from. If you simply add the EXCEPTION section to your existing BEGIN...END block, for instance, you couldn't know which statement generated it, or even if it was really the error you expected.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
BEGIN
MATCH_LEVEL := 1;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
end;
While I'd discourage it, you can catch any other errors in the same exception blocks. However, by definition, those errors would be unexpected, so it would be a poor practice to discard them (you'll never know they even happened!). Generally speaking, if you use a WHEN OTHERS clause in your exception handling, that clause should always conclude with RAISE;, so that the error gets passed up to the next level and is not lost.
Q :
Display all doctors that their charge per
appointment is lower than the minimum.
If exists, display a message to ask these doctors to
increase their charge per appointment by 30%.
Otherwise, provide a message in exception
handlers to notify the user of such result.
Allow the user to enter the minimum charge per
appointment.
the exception doesnt work ??
ACCEPT minchg PROMPT 'Enter the minimum charge per appointment: RM '
DECLARE
ex_min := chgperappt > &minchg EXCEPTION;
v_id doctor.doc_id%TYPE;
v_name doctor.doc_name%TYPE;
v_chg doctor.chgperappt%TYPE;
CURSOR doc_chg IS
SELECT doc_id, doc_name, chgperappt
FROM doctor
WHERE chgperappt < &minchg;
BEGIN
OPEN doc_chg;
LOOP
FETCH doc_chg INTO v_id, v_name, v_chg;
EXIT WHEN doc_chg%NOTFOUND;
DBMS_OUTPUT.PUT_LINE ('Dr. '||v_name||' ('||v_id||') is charging RM '|| v_chg);
DBMS_OUTPUT.PUT_LINE ('Please increase the charge per appointment by 30% --> RM '||v_chg*1.3);
EXCEPTION
WHEN ex_min THEN`enter code here`
DBMS_OUTPUT.PUT_LINE ('All charge per appointment met the minimum criteria.') ;
END LOOP;
CLOSE doc_chg;
END;
/
can you help ?
The basic template for creating a user-defined exception, and handling it, is something like this:
(not your complete code example, but a snippet)
DECLARE
ex_min EXCEPTION;
BEGIN
// open cursor, etc.
IF chgperappt > &minchg THEN
RAISE ex_min;
END IF;
// other code to execute if no exception is raised.
EXCEPTION
WHEN ex_min THEN
// add you code here for exception handling case
END;
Here is a link to a tutorial that goes through the process step-by-step. Hope that helps.