How to retrieve calendaring expression from DB table - plsql

I am making a job with a determined repeat_interval. My goal was to retrieve this value from a table, so that I could change this value in the table afterwards and modify the job accordingly. For this, I made the following trigger:
CREATE OR REPLACE TRIGGER config_table_aur AFTER
UPDATE OF value ON config_table FOR EACH row WHEN (new.property = 'job_interval') DECLARE v_job NUMBER;
BEGIN
dbms_job.submit (v_job, 'begin
update_interval (' || :new.value || ');
end;');
END;
And this trigger calls the following procedure:
CREATE OR REPLACE
PROCEDURE update_interval(
p_new_interval IN config_table.value%type)
AS
BEGIN
dbms_scheduler.set_attribute ('jobu', 'repeat_interval', p_new_interval);
END update_interval;
Where p_new_interval is the value I'm retrieving from the table. The problem that I'm having is that if I try setting a value in the table like this:
FREQ=DAILY; INTERVAL=1;
Then I get an error saying:
Fila 1: ORA-06550: line 2, column 46:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
year month day hour minute second
The symbol ";" was ignored.
ORA-06512: at "SYS.DBMS_JOB", line 82
ORA-06512: at "SYS.DBMS_JOB", line 140
ORA-06512: at "SOMESCHEMA.CONFIG_TABLE_AUR", line 3
ORA-04088: error during execution of trigger 'SOMESCHEMA.CONFIG_TABLE_AUR'
I guess the problem is that the attribute value contains semicolons ';' because if I don't use them I don't get the error.
Do you have any suggestions to circumvent this problem?
Thank you

I guess the problem is that the attribute value contains semicolons ';' because if I don't use them I don't get the error.
Do you have any suggestions to circumvent this problem?
Err ... Your question makes no sense. You know the problem but you don't want to fix the syntax error you have in the repeat_interval calendaring syntax ?
For this simple example your trigger looks unnecessary complex (but you might have a valid reason to use DBMS_JOB though). Here is an example that first sets a scheduled job to run at every minute on 30th second and then later changes the repeat_interval via configuration table to run every 10 seconds:
--
-- scheduler configuration via tailored config table
--
create table scheduler_config (
Job_name varchar2(100) not null,
repeat_interval varchar2(100) not null
);
insert into scheduler_config values('logger1', 'FREQ=SECONDLY; BYSECOND=30');
commit;
create or replace trigger scheduler_config_trg
after update of repeat_interval
on scheduler_config
for each row
declare
pragma autonomous_transaction;
begin
-- Note: throws an exception if no such job
dbms_scheduler.set_attribute(name => :new.job_name,
attribute => 'repeat_interval',
value => :new.repeat_interval);
end;
/
show errors
--
-- a simple job we want to schedule
--
create table scheduler_log (
job_name varchar2(100),
time timestamp(3),
text varchar2(4000)
);
begin
dbms_scheduler.create_job(
job_name => 'logger1',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN insert into scheduler_log values(''logger1'', systimestamp, ''Executed!''); commit; END;',
start_date => systimestamp,
repeat_interval => 'FREQ=SECONDLY; BYSECOND=30',
end_date => null,
enabled => true,
comments => 'Testing configuration');
end;
/
--
-- check how much work has been done and when
--
col job_name for a10
col time for a25
col text for a20
select * from scheduler_log order by time;
--
-- I want more job for my money !
--
update scheduler_config
set repeat_interval = 'FREQ=SECONDLY; INTERVAL=10'
where job_name = 'logger1';
commit;
--
-- remove the job
--
exec dbms_scheduler.drop_job('logger1')

Related

Oracle PL/SQL Script vs SQL in SQL Developer ( Invalid Alter Option)

I am in the process of trying to create multiple PL/SQL Scripts to help maintain our existing databases and allow us to make modifications to them. The first one I'm working on is shown below:
BEGIN
-- add the field to the table that will be used to check against when
-- looking for records that need to be processed.
EXECUTE IMMEDIATE('
ALTER TABLE TBL_SAP_VENDORS_COPY
ADD UPT_TS TIMESTAMP ;
');
-- The automatic update of fields has to be performed using a trigger
-- First we drop the trigger incase it already exists
Execute Immediate ('
drop trigger vendors_updt_mark;
');
EXECUTE IMMEDIATE ('
-- Then add the trigger deffinition
CREATE TRIGGER vendors_updt_mark
before insert or update
on TBL_SAP_VENDORS_COPY
FOR EACH ROW
BEGIN
:new.UPT_TS := SYSTIMESTAMP;
END;
');
END;
I'm having problems with the very 1st statement which is the alter statement:
ALTER TABLE TBL_SAP_VENDORS_COPY
ADD UPT_TS TIMESTAMP ;
When I execute it using the 'Run Script' option in Oracle SQL Developer I get the following error:
Error starting at line : 1 in command -
BEGIN
-- add the field to the table that will be used to check against when
-- looking for records that need to be processed.
EXECUTE ('
ALTER TABLE TBL_SAP_VENDORS_COPY
ADD UPT_TS TIMESTAMP ;
');
-- The automatic update of fields has to be performed using a trigger
-- First we drop the trigger incase it already exists
Execute Immediate ('
drop trigger vendors_updt_mark;
');
EXECUTE IMMEDIATE ('
-- Then add the trigger deffinition
CREATE TRIGGER vendors_updt_mark
before insert or update
on TBL_SAP_VENDORS_COPY
FOR EACH ROW
BEGIN
:new.UPT_TS := SYSTIMESTAMP;
END;
');
END;
Error report -
ORA-06550: line 4, column 5:
PLS-00201: identifier 'EXECUTE' must be declared
ORA-06550: line 4, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Error starting at line : 1 in command -
BEGIN
-- add the field to the table that will be used to check against when
-- looking for records that need to be processed.
EXECUTE IMMEDIATE('
ALTER TABLE TBL_SAP_VENDORS_COPY
ADD UPT_TS TIMESTAMP ;
');
-- The automatic update of fields has to be performed using a trigger
-- First we drop the trigger incase it already exists
Execute Immediate ('
drop trigger vendors_updt_mark;
');
EXECUTE IMMEDIATE ('
-- Then add the trigger deffinition
CREATE TRIGGER vendors_updt_mark
before insert or update
on TBL_SAP_VENDORS_COPY
FOR EACH ROW
BEGIN
:new.UPT_TS := SYSTIMESTAMP;
END;
');
END;
Error report -
ORA-01735: invalid ALTER TABLE option
ORA-06512: at line 4
01735. 00000 - "invalid ALTER TABLE option"
*Cause:
*Action:
When is execute the same statement by itself in SQL Developer is executes with not problem.'
You want:
brackets around the column definition;
to remove the trailing semi-colon in the EXECUTE IMMEDIATE strings when you are executing an SQL command (however, it is required for PL/SQL blocks); and
to catch the exception if the trigger does not exist when you try to drop it.
Like this:
BEGIN
-- add the field to the table that will be used to check against when
-- looking for records that need to be processed.
EXECUTE IMMEDIATE('ALTER TABLE TBL_SAP_VENDORS_COPY ADD ( UPT_TS TIMESTAMP )');
-- The automatic update of fields has to be performed using a trigger
-- First we drop the trigger incase it already exists
DECLARE
trigger_not_found EXCEPTION;
PRAGMA EXCEPTION_INIT( trigger_not_found, -4080 );
BEGIN
Execute Immediate ('drop trigger vendors_updt_mark');
EXCEPTION
WHEN trigger_not_found THEN
NULL;
END;
-- Then add the trigger deffinition
EXECUTE IMMEDIATE ('
CREATE TRIGGER vendors_updt_mark
before insert or update
on TBL_SAP_VENDORS_COPY
FOR EACH ROW
BEGIN
:new.UPT_TS := SYSTIMESTAMP;
END;
');
END;
/
db<>fiddle here

PLSQL is it possible to update a table column on a certain date?

I have this table :
penalities(player,dateofpenality,penalityDays,status)
Based on certain criteria a player is added to this table where the status goes to 'yes'. I want his status to change to 'no' jusrt after the penality period (=penalityDays) is gone.
I thought of a trigger on date but I couldn't find it in plsql.
Please note that I don't want to delete the player as I need the history .
The only way to do this is to schedule a background job which kicks off once a day.
Write a simple stored procedure to apply the change of status:
create or replace procedure update_penalty_status as
begin
update penalties
set status = 'no'
where sysdate >= dateofpenality + penalityDays
and status = 'yes';
commit;
end update_penalty_status;
Then submit a job to run once a day. This uses DBMS_SCHEDULER (available since 10g) rather than the older DBMS_JOB.
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'your_schema.my_job1',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN update_penalty_status; END;',
start_date => trunc(sysdate)+1,
repeat_interval => 'FREQ=DAILY'
enabled => TRUE,
comments => 'Revoke expired penalties');
END;
/
Note that you will need the CREATE JOB privilege to do this. You may need to ask your DBA for help and guidance. Normally I would link to the Oracle documentation at this point, but I think the Oracle-Base article is a friendlier place to start.

Highest record in PL/SQL

I have created a query that displays highest returned items from a table. My query it works perfectly with no errors! However, I want an efficient way of converting the query to PL/SQL block. The purpose of conversion is to handle errors.
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (
SELECT MAX(ITEM_NUM) FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
Here's a way we do some exception handling in packages. I altered it to use your code as an example. Maybe you can use some ideas from it.
At the top, set a CONSTANT to the name of the procedure, and some variables to catch Oracle SQL error number and message in. Then in the body of the procedure, there is an anonymous block containing the select. First we set a variable to indicate where we are (err_loc) in case there are multiple locations where an error could be caught in one block. Then the select is issued. If an error occurs, it's caught by the EXCEPTION clause. Error info from Oracle is caught in the err* variables, the err_string is built and then emailed via the UTL_MAIL package. RAISE raises the error so the program halts. It's set up like this to be generic as possible, we can drop in the template, change the MBR_NAME, SQL, err_loc and that's it.
PROCEDURE TEST_PROC AS
MBR_NAME CONSTANT VARCHAR2(100) := 'TEST_PROC'; -- For use in error handling. Package member name.
err_nbr NUMBER; -- Holds a SQL error number if an exception occurs.
err_msg VARCHAR2(1000); -- Holds a SQL error message if an exception occurs.
err_string VARCHAR2(2000);
BEGIN
BEGIN
err_loc := 'Selecting max quantity'; -- Email subject
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (SELECT MAX(ITEM_NUM)
FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
EXCEPTION
WHEN OTHERS THEN
err_nbr := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 1000);
err_string := 'ERROR: ' || err_nbr || ' occurred: ' || err_msg;
-- PKG_NAME and err_email_recip set in the body.
UTL_MAIL.send(sender => PKG_NAME||'.'||MBR_NAME||'#yourcompany.com',
recipients => err_email_recip,
subject => 'ERROR '|| err_loc,
message => CHR(13))||err_string);
RAISE;
END;
END TEST_PROC;
Have a look at cursors and records.
That way you have fetch data from the query and process the line if needed.
I don't have a database by hand to test my code, but this might give you an idea how a cursor and record work.
In order to capture a EXCEPTION you could add an exception handler and let it log the record you where busy with when the exception occured.
DECLARE
CURSOR CursorName IS
SELECT ColumnOne
FROM TableA
WHERE Name = 'Me';
RecordNumber CursorName%ROWTYPE;
BEGIN
-- Fetch the records from the cursor.
OPEN CursorName;
LOOP
FETCH CursorName INTO RecordNumber;
-- Do something with the record.
EXIT WHEN CursorName %NOTFOUND;
END LOOP;
CLOSE CursorName;
END;
/
Adding a error handeling would be done right above END:
EXCEPTION
WHEN OTHERS THEN
-- Log error message.
END;
/
Link: Error handeling
Does that answer you question a bit?

Encountered the symbol "." when expecting one of the following

create or replace procedure zivtest is
utl_mail.send_attach_varchar2 varchar2(100) :=utl_mail;
mail_subject varchar2(255) := 'zivtest'||sysdate;
mail_message varchar2(1000):='zivtest'
||'<br>'||'User count of all active users in all Realms';
recipients_list varchar2(255) :='user#user.com';
p_sender varchar2(50) := 'user#user.com';
data clob;
data_header varchar2(255);
--E_too_much_values exception;
begin
for item IN ( select decode(r.irl_id,1,'UPSEC',2,'PC',3,'CSM',6,'DMROAM',7,'WORKFLOW',8,'CSS_DEALER',10,'CCBATCH',14,'CSS',61,'COL',81,'CRM'
,82,'FIU',83,'OCM',84,'BOA',127,'SAPI') realm,
count (a.user_id) users
from sec_idm_user a , SEC_IDM_REALM_A r
where a.iu_rlid=r.irl_id
and a.iu_lock='N'
and a.iu_last_login >= sysdate -90
group by r.irl_id) LOOP
if data is not null then
utl_mail.send_attach_varchar2(
sender => p_sender,
recipients => recipients_list,
subject => mail_subject,
message => mail_message,
mime_type => 'text/html; charset=WINDOWS-1255',
attachment => data_header || data ,
att_filename => 'USER_COUNT.csv');
end if ;
END LOOP;
END;
How can I fix this?
I got the following error when I try to compile it:
PLS-00103: Encountered the symbol "." when expecting one of the following:
<an identifier> <a double-quoted delimited-identifier> current
You are seeing this error because second line
utl_mail.send_attach_varchar2 varchar2(100) :=utl_mail;
doesn't make sense. you are trying to declare variable named utl_mail.send_attach_varchar2.
What you need is to install utl_mail package, to be able to use procedure utl_mail.send_attach_varchar2

Oracle trigger call procedure which starts a workflow

I have the following issue:
I created a trigger which is activated when a row is inserted into the table.
The trigger then starts a procedure.
The procedure then starts a workflow.
When I start the procedure it works fine. But when I start the trigger by entering a new row I got the following error:
Zeile 12: ORA-20001: Task not found - Please check the Task Type, Name and Location are correct.
ORA-06512: in "OWBSYS.WB_RT_API_EXEC", Zeile 759
ORA-06512: in "OWB***.EXECUTE_WF_ABC", Zeile 10
ORA-06512: in "OWB***.START_EXECUTE_WF_ABC", Zeile 7
ORA-06512: in "OWB***.ABC_WORKFLOW", Zeile 2
This is my trigger:
create or replace
TRIGGER ABC_WORKFLOW
BEFORE INSERT
ON OWB***.STG_ABC
FOR EACH ROW
BEGIN
OWB***.EXECUTE_WF_ABC;
END ;
This is my procedure:
create or replace
PROCEDURE EXECUTE_WF_ABC
AS
status NUMBER;
-- paramlist VARCHAR2(30000 CHAR);
BEGIN
owbsys.wb_rt_script_util.set_workspace ('OWBREPOWN.OWB***');
status :=
owbsys.wb_rt_api_exec.run_task (
p_location_name => 'OWF_LOCATION',
p_task_type => 'PROCESS',
p_task_name => 'WF_ABC',
-- p_custom_params => paramlist ,
p_system_params => '',
p_oem_friendly => 0 ,
p_background => 1); -- execute in background
DBMS_OUTPUT.put_line (status);
COMMIT;
--- EXCEPTION
--- when others then
--- message;
--- null;
END;
Did I miss something here?
Thanks in advance.
if your table is the TASK table, then your trigger is firing BEFORE the row exists, so if that workflow is checking for data in the row you're inserting then there is no wonder it's failing.
also COMMIT; in a trigger is really bad (and would fail if you inserted more than 1 row).

Resources