Oracle Form Personalization Calling Custom Procedure - oracle

I am doing form personalization in HR module .my requirement is when the end-user is trying to terminate than a procedure should get executed and check the condition if the is not met then it should display an error in the front end form
I am using this procedure:=
CREATE OR REPLACE PROCEDURE validate_terminate IS
u_id NUMBER := fnd_global.user_id;
e_id NUMBER;
p_id NUMBER;
l_yes VARCHAR2 (1);
null_found EXCEPTION;
BEGIN
SELECT EMPLOYEE_ID
INTO e_id
FROM fnd_user
WHERE user_id = u_id;
SELECT person_id
INTO p_id
FROM per_all_people_f
WHERE person_id = e_id;
SELECT 'Y'
INTO l_yes
FROM DUAL
WHERE EXISTS
(SELECT 'Y'
FROM pa_expenditure_items_all paei,
pa_expenditures_all pae
WHERE paei.expenditure_id = pae.expenditure_id
AND pae.incurred_by_person_id = p_id
AND paei.cost_distributed_flag = 'N');
IF l_yes = NULL THEN
dbms_output.put_line ('USER CANNOT PERFORM TERMINATE');
RAISE null_found;
END IF;
EXCEPTION
WHEN null_found THEN
dbms_output.put_line ('CONDITION IS NOT MET');
dbms_output.put_line ('CANNOT PROCESS TERMINATE');
WHEN no_data_found THEN
dbms_output.put_line ('CONDITION IS NOT MET');
dbms_output.put_line ('CANNOT PROCESS TERMINATE');
END;
The procedure is working fine but I am unable to execute it in the front end
as I am doing these below are the screenshots
[front end from][1]
[ACTION tab image][2]
but the procedure is not getting executed.

dbms_output.put_line is only in pl/sql visible if serveroutput is on.
You won't see these messages in oracle forms.
You should use message or an alert followed by
raise form_trigger_failure

Related

Passing data when query returns value and No "EXCEPTION WHEN NO_DATA_FOUND THEN" (Oracle 11g)

I have created a procedure for updating my t_ritm table. First I have select rrcd_qnty (which is my product quantity) of a product id from t_rrcd table. Then I update the rrcd_qnty value in t_ritm table.
Here is my procedure:
procedure update_ritm_new_rate(p_oid in varchar2, p_ritm_rate in varchar2, p_euser in varchar2)
is
nrate varchar2(4);
begin
SELECT rrcd_rate into nrate
FROM (select oid, t_rrcd.rrcd_rate
from t_rrcd
where rrcd_ritm= p_oid
ORDER BY oid DESC )
WHERE rownum <= 1
ORDER BY rownum DESC ;
EXCEPTION
WHEN NO_DATA_FOUND THEN nrate := 0;
update t_ritm
set ritm_rate = nrate, euser = p_euser, edat = sysdate
where oid = p_oid;
commit;
end update_ritm_new_rate;
Some of my product id Quantity was null. so I was getting No_Data_Found error. But when and which product id has Quantity value they were successfully updating. For avoiding No_Data_Found I used EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0; which solved my no_Data_Found error. But when product id has quantity value they were not updating.
I had search lot of for this issue but not get good solution. What should be the best practice for avoiding No_Data_Found error? Could I pass my value if I don't get any No_Data_Found error?
thank in advance
That's because - if your SELECT returns something, it never reaches UPDATE as it is hidden behind the EXCEPTION handler.
Therefore, enclose it (SELECT) into its own BEGIN-END block, and put UPDATE out of it so that it is executed with whichever NRATE value is used.
PROCEDURE update_ritm_new_rate (p_oid IN VARCHAR2,
p_ritm_rate IN VARCHAR2,
p_euser IN VARCHAR2)
IS
nrate VARCHAR2 (4);
BEGIN
BEGIN --> this
SELECT rrcd_rate
INTO nrate
FROM ( SELECT oid, t_rrcd.rrcd_rate
FROM t_rrcd
WHERE rrcd_ritm = p_oid
ORDER BY oid DESC)
WHERE ROWNUM <= 1
ORDER BY ROWNUM DESC;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
nrate := 0;
END; --> this
UPDATE t_ritm
SET ritm_rate = nrate, euser = p_euser, edat = SYSDATE
WHERE oid = p_oid;
COMMIT;
END update_ritm_new_rate;
I have fixed the issue by adding EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0; after the update query.
procedure update_ritm_new_rate(p_oid in varchar2, p_ritm_rate in varchar2, p_euser in varchar2)
is
nrate varchar2(4);
begin
SELECT rrcd_rate into nrate FROM (select oid, t_rrcd.rrcd_rate from t_rrcd where rrcd_ritm= p_oid ORDER BY oid DESC )
WHERE rownum <= 1 ORDER BY rownum DESC ;
update t_ritm set ritm_rate = nrate, euser = p_euser, edat = sysdate where oid = p_oid;
commit;
EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0;
end update_ritm_new_rate;

Return success/failure and error message of transaction?

I need to have an update and insert wrapped into a transaction that will be called from an external web application. I'm trying to get back a basic success/failure status when it is called along with an error message if it failed.
What I would like to do is something like the following, but it gives me the following errors:
INTO clause is expected in this SELECT statement
"SQLCODE": invalid identifier
DECLARE STATUS VARCHAR2(128);
MESSAGE VARCHAR2(128);
BEGIN
UPDATE MYTABLE
SET COL1 = 400
WHERE USERNAME = 'bigtunacan' AND pk = 12345;
INSERT INTO MYTABLE (username, col1, col2)
VALUES('bigtunacan', 400, 'foo');
SELECT 'TRUE' AS STATUS, '' AS MSG FROM MYTABLE WHERE ROWNUM = 1;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
SELECT 'FALSE' AS STATUS, SQLCODE || SQLERRM AS MSG FROM MYTABLE WHERE ROWNUM = 1;
END;
Any Select statement inside a PL/SQL code needs an INTO clause, except the ones called from or within a cursor. In your case, You do not need to invoke any Select statement, but just assign static string values ('TRUE','FALSE') or pseudocolumns independent from SQL such as sqlcode or sqlerrm to your already defined variables.
So, consider using :
DECLARE
STATUS VARCHAR2(128) := 'TRUE';
MESSAGE VARCHAR2(128);
BEGIN
UPDATE MYTABLE
SET COL1 = 400
WHERE USERNAME = 'bigtunacan' AND pk = 12345;
INSERT INTO MYTABLE (username, col1, col2)
VALUES('bigtunacan', 400, 'foo');
-- SELECT 'TRUE' AS STATUS, '' AS MSG FROM MYTABLE WHERE ROWNUM = 1;
-- completely remove this above row, STATUS is already initialized as TRUE
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
STATUS := 'FALSE';
RAISE_APPLICATION_ERROR(-20333,'Caution : An error was encountered -
'||SQLCODE||' -ERROR- '||SQLERRM);
END;
A Commit/Rollback should not be ideally included inside a called procedure. From Tom Kyte's own words:
I wish PLSQL didn't support commit/rollback. I firmly believe transaction control MUST be done at the topmost, invoker level.
You should consider converting your anonymous block into a procedure and define the transaction control in the invoker's code.
CREATE OR REPLACE procedure yourprocedure
( p_status OUT VARCHAR2,
p_message OUT VARCHAR2
) AS
BEGIN
UPDATE mytable
SET
col1 = 400
WHERE username = 'bigtunacan' AND pk = 12345;
INSERT INTO MYTABLE (username, col1, col2)
VALUES('bigtunacan', 400, 'foo');
p_status := 'TRUE' ;
p_message := NULL;
EXCEPTION
WHEN OTHERS THEN
p_status := 'FALSE' ;
p_message := SQLERRM ;
END;
/
Invocation ( May be another block, procedure or application layer )
DECLARE
l_status VARCHAR2(20);
l_message VARCHAR2(400);
BEGIN
yourprocedure(l_status,l_message);
IF
l_status = 'TRUE'
THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END;
/
Only exception is if it's defined as an Autonomous procedure (mostly used for logging purposes) where you are supposed to commit inside the procedure.

Exception dose not working in execute immediate insert in oracle

I have some problem in execute immediate insert statement exception part.
I have a table query_tb that contains two columns (DEPT and SOURCE_VALUE)
The column contains data in below
CLERK
select a.empno,a.ename,a.job,a.mgr,a.hiredate,b.deptno,b.dname,b.loc
from emp a,dept b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
SALESMAN
select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e,dept
d where e.deptno=d.deptno and e.empno= '#V_GCIF#'
MANAGER
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
ADMIN
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
If I pass the correct empno which is keep on the emp table it runs fine. But if I pass the incorrect empno (no data) then exception part not working.
create or replace
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
CURSOR rec
IS
SELECT dept,
source_value
FROM query_tb;
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
END IF;
END LOOP;
commit;
EXCEPTION
WHEN others THEN
dbms_output.put_line('No data Found...');
END;
That's because you are not running a select command, it is an insert command (insert select) which means that if the select won't return rows it just doesn't insert anything and no error is thrown for that. You should check whether the insert command has affected any rows. The way you do that in Oracle is checking the SQL%ROWCOUNT immediate after the execution, if it turns to be 0 then you do your job raising an exception. It would be something like:
DECLARE
customException EXCEPTION;
PRAGMA EXCEPTION_INIT( customException, -20001 );
....
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc)
('||v_query|| ')';
IF (SQL%ROWCOUNT) = 0 then
raise_application_error( -20001, 'This is a custom error' );
end if;
EXCEPTION
WHEN customException THEN
dbms_output.put_line('No data Found...');
END;
It's a long time without programming in Oracle PLSql So somethings there on the provided code may not compile but it is all there look into it in the internet and you will be good.
Exception does not work because there is no exception.
If SELECT returns NULL then 0 rows will be INSERT.
Example:
insert into t1(c1)
select 100 from dual where 1=0;
Result: 0 rows inserted.
insert into ... select from..
will not raise a no_data_found exception. It will just insert 0 records.
To test if you have inserted any records you can use SQL%ROWCOUNT after the insert statement.
execute immedate 'INSERT...;
if SQL%ROWCOUNT=0
then
dbms_output.put_line('no records inserted');
else
...
end if;
Also you might want to consider changing #V_GCIF# into something that can be used as a bind varaiable such as :P_ID.
You can skip the replace statement and change the execute immediate into something like:
execute immediate 'INSERT INTO ...'||v_query
using p_id;
This will bind the value of p_id to the :p_id in the statement.
The very basic thing here is to capture the error appropriately so that you can able to track back. Here i have put loggers and EXCEPTION handlers jsut to check the error. Also i have put a RAISE_APPLICATION_ERROR. Did some modifications too in the snipet. Hope it helps.
PS: Have not checked for the syntax as i dont have workspace now with me.
CREATE OR REPLACE
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
--Not needed
-- CURSOR rec
-- IS
-- SELECT dept,
-- source_value
-- FROM query_tb;
--Not needed
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for Clerk--> '||SQLCODE||'---'||SQLERRM);
END;
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for other then clerk --> '||SQLCODE||'---'||SQLERRM);
END;
END IF;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Error occurred in the plsql block',TRUE);
END;
/

Oracle, drop table if it exists AND empty

I need to drop an Oracle table only if it 1) exists AND 2) Is NOT Empty
I wrote this code but if the table does not exist the code does not work:
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
END;
/
What am I doing wrong? Please help.
Many thanks in advance
If you want to drop a table if it exists and empty(as the title of the question states) you could do this as follows:
create or replace procedure DropTableIfEmpty(p_tab_name varchar2)
is
l_tab_not_exists exception;
pragma exception_init(l_tab_not_exists, -942);
l_is_empty number;
l_query varchar2(1000);
l_table_name varchar2(32);
begin
l_table_name := dbms_assert.simple_sql_name(p_tab_name);
l_query := 'select count(*)
from ' || l_table_name ||
' where rownum = 1';
execute immediate l_query
into l_is_empty;
if l_is_empty = 0
then
execute immediate 'drop table ' || l_table_name;
dbms_output.put_line('Table "'|| p_tab_name ||'" has been dropped');
else
dbms_output.put_line('Table "'|| p_tab_name ||'" exists and is not empty');
end if;
exception
when l_tab_not_exists
then dbms_output.put_line('Table "'|| p_tab_name ||'" does not exist');
end;
When you are trying to drop a table, or query a table, which does not exist, Oracle will raise ORA-00942 exception and execution of a pl/sql block halts. We use pragma exception_init statement to associate ORA-00942 exception with our locally defined exception l_tab_not_exists in order to handle it appropriately.
Test case:
SQL> exec droptableifempty('tb_test'); -- tb_test table does not exists
Table "tb_test" does not exist
SQL> create table tb_test(
2 col number
3 );
table TB_TEST created.
SQL> exec droptableifempty('tb_test');
Table "tb_test" has been dropped
As a side note. Before querying num_rows column of [dba][all][user]_tables in order to determine number of rows a table has, you need to gather table statistic by executing dbms_stats.gather_table_stats(user, '<<table_name>>');, otherwise you wont get the actual number of rows.
In PL/SQL it is 'normal' to catch the exception.
If it is the correct exception then continue with the next part of your code.
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('OH DEAR AN EXCEPTION WAS THROWN DUE TO' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('THE ORACLE CODE IS ' || SQLCODE);
-- if it is the oracle code for no such table, or no data selected
-- everything is fine.
END;
Of course it won't work if the table doesn't exist. Your second select would get a "No data found" exception, and you're not doing any exception handling. At least you should move the second select inside the first IF block. Best to add exception handling.
here is an easy way to solve this problem:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE [sssss]';
EXCEPTION WHEN OTHERS THEN NULL;
END;

Check table exist or not before create it in Oracle

Trying to check is table exist before create in Oracle. Search for most of the post from Stackoverflow and others too. Find some query but it didn't work for me.
IF((SELECT count(*) FROM dba_tables where table_name = 'EMPLOYEE') <= 0)
THEN
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)
END IF;
Which gives me error
Error: ORA-00900: invalid SQL statement
SQLState: 42000
ErrorCode: 900
Position: 1
I search for the syntax for IF condition, I think which is also write.
Please suggest me....
As Rene also commented, it's quite uncommon to check first and then create the table.
If you want to have a running code according to your method, this will be:
declare
nCount NUMBER;
v_sql LONG;
begin
SELECT count(*) into nCount FROM dba_tables where table_name = 'EMPLOYEE';
IF(nCount <= 0)
THEN
v_sql:='
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
END IF;
end;
But I'd rather go catch on the Exception, saves you some unnecessary lines of code:
declare
v_sql LONG;
begin
v_sql:='create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -955 THEN
NULL; -- suppresses ORA-00955 exception
ELSE
RAISE;
END IF;
END;
/
I know this topic is a bit old, but I think I did something that may be useful for someone, so I'm posting it.
I compiled suggestions from this thread's answers into a procedure:
CREATE OR REPLACE PROCEDURE create_table_if_doesnt_exist(
p_table_name VARCHAR2,
create_table_query VARCHAR2
) AUTHID CURRENT_USER IS
n NUMBER;
BEGIN
SELECT COUNT(*) INTO n FROM user_tables WHERE table_name = UPPER(p_table_name);
IF (n = 0) THEN
EXECUTE IMMEDIATE create_table_query;
END IF;
END;
You can then use it in a following way:
call create_table_if_doesnt_exist('my_table', 'CREATE TABLE my_table (
id NUMBER(19) NOT NULL PRIMARY KEY,
text VARCHAR2(4000),
modified_time TIMESTAMP
)'
);
I know that it's kinda redundant to pass table name twice, but I think that's the easiest here.
Hope somebody finds above useful :-).
Please try:
SET SERVEROUTPUT ON
DECLARE
v_emp int:=0;
BEGIN
SELECT count(*) into v_emp FROM dba_tables where table_name = 'EMPLOYEE';
if v_emp<=0 then
EXECUTE IMMEDIATE 'create table EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
END;
declare n number(10);
begin
select count(*) into n from tab where tname='TEST';
if (n = 0) then
execute immediate
'create table TEST ( ID NUMBER(3), NAME VARCHAR2 (30) NOT NULL)';
end if;
end;
My solution is just compilation of best ideas in thread, with a little improvement.
I use both dedicated procedure (#Tomasz Borowiec) to facilitate reuse, and exception handling (#Tobias Twardon) to reduce code and to get rid of redundant table name in procedure.
DECLARE
PROCEDURE create_table_if_doesnt_exist(
p_create_table_query VARCHAR2
) IS
BEGIN
EXECUTE IMMEDIATE p_create_table_query;
EXCEPTION
WHEN OTHERS THEN
-- suppresses "name is already being used" exception
IF SQLCODE = -955 THEN
NULL;
END IF;
END;
BEGIN
create_table_if_doesnt_exist('
CREATE TABLE "MY_TABLE" (
"ID" NUMBER(19) NOT NULL PRIMARY KEY,
"TEXT" VARCHAR2(4000),
"MOD_TIME" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
');
END;
/
Any solution which relies on testing before creation can run into a 'race' condition where another process creates the table between you testing that it does not exists and creating it. - Minor point I know.
-- checks for table in specfic schema:
declare n number(10);
begin
Select count(*) into n from SYS.All_All_Tables where owner = 'MYSCHEMA' and TABLE_NAME = 'EMPLOYEE';
if (n = 0) then
execute immediate
'create table MYSCHEMA.EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
end;
Well there are lot of answeres already provided and lot are making sense too.
Some mentioned it is just warning and some giving a temp way to disable warnings. All that will work but add risk when number of transactions in your DB is high.
I came across similar situation today and here is very simple query I came up with...
declare
begin
execute immediate '
create table "TBL" ("ID" number not null)';
exception when others then
if SQLCODE = -955 then null; else raise; end if;
end;
/
955 is failure code.
This is simple, if exception come while running query it will be suppressed. and you can use same for SQL or Oracle.
Its no need declare and count apply too.
begin
for rec in (select 1 from user_tables where table_name = 'YOUR_TABLE')
-- or
-- for rec in (select 1 from all_tables where table_name = 'YOUR_TABLE' and owner = 'YOU')
loop
execute immediate 'create table your_table as (f1 char(1))';
end loop;
end;
/
Will be good mode create check function
create or replace function this_object_exists (p_obj_name user_objects.object_name%type) return boolean
is
begin
for rec in (select 1 from user_objects where object_name = upper(p_obj_name))
loop
return true;
end loop;
return false;
end this_object_exists;
And thus use code for check exists
.
.
.
.
INDEX PARTITION
TABLE SUBPARTITION
SEQUENCE
TABLE PARTITION
PROCEDURE
LOB PARTITION
LOB
INDEX SUBPARTITION
PACKAGE
PACKAGE BODY
TYPE BODY
TRIGGER
INDEX
TABLE
VIEW
FUNCTION
SYNONYM
TYPE
JOB
...
begin
if not this_object_exists('your_table') then
execute immediate 'create table your_table as (f1 char(1))';
end if;
end;
or
begin
if this_object_exists('your_table') then
execute immediate 'drop table your_table';
end if;
execute immediate 'create table your_table as (f1 char(1))';
end;

Resources