Need to find the number of times PLSQL function gets executed - oracle

There is a requirement from client side that after function get executed more than 2 times then its output should be concatinated with some string and this function is inside a package .
for example...
function get called 7 times from package (its a backend job which executed automatically) and returns 'abc' but when the job runs for the 3rd time i want output 'abcde'.

One option is to create a separate log table and insert a row for each of function calls; then - within a function - check how many times it was invoked and return appropriate output. Something like this:
Log table:
SQL> CREATE TABLE flog
2 (
3 cuser VARCHAR2 (30),
4 sid NUMBER
5 );
Table created.
Package:
SQL> CREATE OR REPLACE PACKAGE pkg_test
2 IS
3 FUNCTION f_test
4 RETURN VARCHAR2;
5
6 PROCEDURE p_test;
7 END;
8 /
Package created.
Package body:
SQL> CREATE OR REPLACE PACKAGE BODY pkg_test
2 IS
3 FUNCTION f_test
4 RETURN VARCHAR2
5 IS
6 l_cnt NUMBER;
7 retval VARCHAR2 (10);
8 BEGIN
9 SELECT COUNT (*)
10 INTO l_cnt
11 FROM flog
12 WHERE cuser = USER
13 AND sid = SYS_CONTEXT ('USERENV', 'SID');
14
15 retval := CASE WHEN l_cnt <= 2 THEN 'abc' ELSE 'abc' || 'de' END;
16 RETURN retval;
17 END;
18
19 PROCEDURE p_test
20 IS
21 BEGIN
22 FOR i IN 1 .. 3
23 LOOP
24 INSERT INTO flog (cuser, sid)
25 VALUES (USER, SYS_CONTEXT ('USERENV', 'SID'));
26
27 DBMS_OUTPUT.put_line ('Execution #' || i || ', result = ' || f_test);
28 END LOOP;
29 END;
30 END pkg_test;
31 /
Package body created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> EXEC pkg_test.p_test;
Execution #1, result = abc
Execution #2, result = abc
Execution #3, result = abcde
PL/SQL procedure successfully completed.
SQL>

Related

Need to fetch the table details using stored procedure when we give table name as input

CREATE TABLE test_table
(
col1 NUMBER(10),
col2 NUMBER(10)
);
INSERT INTO test_table
VALUES (1, 2);
I am writing a stored procedure wherein if I give a table name as an input, that should give me the table data and column details.
For example:
SELECT *
FROM <input_table_name>;
But this causes an error that the SQL command has not ended properly even though I have taken care of this.
My attempt:
CREATE OR REPLACE PROCEDURE sp_test(iv_table_name IN VARCHAR2,
p_out_cur OUT SYS_REFCURSOR)
AS
lv_str VARCHAR2(400);
lv_count NUMBER(1);
lv_table_name VARCHAR2(255):=UPPER(iv_table_name);
BEGIN
lv_str := 'SELECT * FROM '||lv_table_name;
SELECT COUNT(1) INTO lv_count FROM all_tables WHERE table_name = lv_table_name;
IF lv_count = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
OPEN p_out_cur FOR lv_str;
END IF;
END sp_test;
Tool used: SQL developer(18c)
In dynamic SQL, you do NOT terminate statement with a semicolon.
EXECUTE IMMEDIATE 'SELECT * FROM '||lv_table_name||';';
-----
remove this
Anyway, you won't get any result when you run that piece of code. If you really want to see table's contents, you'll have to switch to something else, e.g. create a function that returns ref cursor.
Sample data:
SQL> SELECT * FROM test_table;
COL1 COL2
---------- ----------
1 2
3 4
Procedure you wrote is now correct:
SQL> CREATE OR REPLACE PROCEDURE sp_test (iv_table_name IN VARCHAR2,
2 p_out_cur OUT SYS_REFCURSOR)
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 BEGIN
8 lv_str := 'SELECT * FROM ' || lv_table_name;
9
10 SELECT COUNT (1)
11 INTO lv_count
12 FROM all_tables
13 WHERE table_name = lv_table_name;
14
15 IF lv_count = 0
16 THEN
17 DBMS_OUTPUT.put_line ('Table does not exist');
18 ELSE
19 OPEN p_out_cur FOR lv_str;
20 END IF;
21 END sp_test;
22 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 l_col1 NUMBER (10);
4 l_col2 NUMBER (10);
5 BEGIN
6 sp_test ('TEST_TABLE', l_rc);
7
8 LOOP
9 FETCH l_rc INTO l_col1, l_col2;
10
11 EXIT WHEN l_rc%NOTFOUND;
12
13 DBMS_OUTPUT.put_line (l_col1 || ', ' || l_col2);
14 END LOOP;
15 END;
16 /
1, 2 --> contents of the
3, 4 --> TEST_TABLE
PL/SQL procedure successfully completed.
SQL>
A function (instead of a procedure with the OUT parameter):
SQL> CREATE OR REPLACE FUNCTION sf_test (iv_table_name IN VARCHAR2)
2 RETURN SYS_REFCURSOR
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 l_rc SYS_REFCURSOR;
8 BEGIN
9 lv_str := 'SELECT * FROM ' || lv_table_name;
10
11 SELECT COUNT (1)
12 INTO lv_count
13 FROM all_tables
14 WHERE table_name = lv_table_name;
15
16 IF lv_count = 0
17 THEN
18 raise_application_error (-20000, 'Table does not exist');
19 ELSE
20 OPEN l_rc FOR lv_str;
21 END IF;
22
23 RETURN l_rc;
24 END sf_test;
25 /
Function created.
Testing:
SQL> SELECT sf_test ('liksajfla') FROM DUAL;
SELECT sf_test ('liksajfla') FROM DUAL
*
ERROR at line 1:
ORA-20000: Table does not exist
ORA-06512: at "SCOTT.SF_TEST", line 18
SQL> SELECT sf_test ('TEST_TABLE') FROM DUAL;
SF_TEST('TEST_TABLE'
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
COL1 COL2
---------- ----------
1 2
3 4
SQL>

How can ı create table with function?

STEP 1:
Creating a new table and adding a record to the database with the help of the procedure
• the function will take 3 parameters string ,string and number,
• The first parameter represents the name of the table, and the other two parameters represent the records to be added to the created table.
Step 2:
As seen in the screenshot below; As described above, when the function runs, a table named 'mytable' will be created in the database with the first parameter, and other parameters will be added to the created table as a record.
enter image description here
Why function? They are supposed to return some value, not to perform DDL. Therefore, a procedure it is, using dynamic SQL as otherwise it won't work.
SQL> CREATE OR REPLACE PROCEDURE p_test (par_table_name IN VARCHAR2,
2 par_str IN VARCHAR2,
3 par_num IN NUMBER)
4 IS
5 l_cnt NUMBER;
6 BEGIN
7 SELECT COUNT (*)
8 INTO l_cnt
9 FROM user_tables
10 WHERE UPPER (table_name) = UPPER (par_table_name);
11
12 IF l_cnt = 0
13 THEN
14 EXECUTE IMMEDIATE 'create table '
15 || par_table_name
16 || ' as select '
17 || DBMS_ASSERT.enquote_literal (par_str)
18 || ' col_string, '
19 || par_num
20 || ' col_number from dual';
21 ELSE
22 raise_application_error (-20000,
23 'Object with that name already exists');
24 END IF;
25 END;
26 /
Procedure created.
Testing:
SQL> exec p_test('little', 'foot', 20);
PL/SQL procedure successfully completed.
SQL> select * from little;
COL_ COL_NUMBER
---- ----------
foot 20
SQL>

Oracle Sql Function Compiled (with errors)

i am using below function to change the password ,
it give me compilation error once i compile
CREATE or replace FUNCTION updatePassword(CurrentP VARCHAR2,NewPwd VARCHAR2,StudentId VARCHAR2) RETURN VARCHAR2
is
getCount integer :=0;
BEGIN
Select count(*) into getCount from users where student_id=StudentId and Password=md5(CurrentP);
if getCount == 1
then
update users set Password=md5(NewPwd) where student_id=StudentId and Password=md5(CurrentP);
else
getCount = 0
RETURN getCount;
END;
/
i want to return getCount value on successfully update password else it return 0
below is error mention
UPDATEPASSWORD Compiled (with errors)
You have some PL/SQL syntax issues
CREATE or replace FUNCTION updatePassword(CurrentP VARCHAR2,NewPwd VARCHAR2,StudentId VARCHAR2) RETURN VARCHAR2
is
getCount integer :=0;
BEGIN
Select count(*) into getCount from users where student_id=StudentId and Password=md5(CurrentP);
if getCount = 1
then
update users set Password=md5(NewPwd) where student_id=StudentId and Password=md5(CurrentP);
else
getCount := 0;
end if;
RETURN getCount;
END;
/
You have a little syntax error. The code should look likes this:
CREATE or replace FUNCTION updatePassword(
CurrentP in VARCHAR2,
NewPwd in VARCHAR2,
StudentId in number) RETURN number
is
getCount number:=0;
BEGIN
Select count(*) into getCount from users where student_id=StudentId and Password=md5(CurrentP);
if getCount = 1
then
update users set Password=md5(NewPwd) where student_id=StudentId and Password=md5(CurrentP);
end if;
RETURN getCount;
END;
UPDATE:
After a closer look, you have a lot of syntax problems^^ Modified the code.
Suggestions:
Avoid stand alone functions/procedures. Use the Oracle advantage - packages. Helps to avoid invalidated objects situation;
When you use PL/SQL - try to stick with the coding standards in PL/SQL.
Set the correct types of variables.
Here's an example which shows how to do that.
First, test case. The MD5 function doesn't do anything; it just returns the IN parameter's value.
SQL> CREATE TABLE users
2 (
3 student_id VARCHAR2 (10),
4 password VARCHAR2 (20)
5 );
Table created.
SQL> INSERT INTO users (student_id, password)
2 VALUES ('100', 'Stack');
1 row created.
SQL> CREATE OR REPLACE FUNCTION md5 (par_password IN VARCHAR2)
2 RETURN VARCHAR2
3 IS
4 BEGIN
5 RETURN par_password;
6 END;
7 /
Function created.
SQL>
You should use a procedure to modify a password, not a function as you can't perform DML in a function (OK, you can, but you shouldn't. I'll show you show to do that later).
The procedure returns number of updated rows; if you want, you can return getcount (which is number of selected rows).
SQL> CREATE OR REPLACE PROCEDURE updatepassword (p_currentp IN VARCHAR2,
2 p_newpwd IN VARCHAR2,
3 p_studentid IN VARCHAR2,
4 retval OUT NUMBER)
5 IS
6 getcount INTEGER := 0;
7 BEGIN
8 SELECT COUNT (*)
9 INTO getcount
10 FROM users
11 WHERE student_id = p_studentid
12 AND password = md5 (p_currentp);
13
14 IF getcount = 1
15 THEN
16 UPDATE users
17 SET password = md5 (p_newpwd)
18 WHERE student_id = p_studentid
19 AND password = md5 (p_currentp);
20
21 retval := SQL%ROWCOUNT;
22 END IF;
23 END;
24 /
Procedure created.
SQL>
Testing: change password from "Stack" to "Overflow":
SQL> SET SERVEROUTPUT ON;
SQL>
SQL> DECLARE
2 l_retval NUMBER;
3 BEGIN
4 updatepassword ('Stack',
5 'Overflow',
6 '100',
7 l_retval);
8 DBMS_OUTPUT.put_line ('retval = ' || l_retval);
9 END;
10 /
retval = 1
PL/SQL procedure successfully completed.
SQL> SELECT * FROM users;
STUDENT_ID PASSWORD
---------- --------------------
100 Overflow
SQL>
Seems to be OK.
And here's why you can't use a function:
SQL> DROP PROCEDURE updatepassword;
Procedure dropped.
SQL> CREATE OR REPLACE FUNCTION updatepassword (p_currentp IN VARCHAR2,
2 p_newpwd IN VARCHAR2,
3 p_studentid IN VARCHAR2)
4 RETURN NUMBER
5 IS
6 getcount INTEGER := 0;
7 BEGIN
8 SELECT COUNT (*)
9 INTO getcount
10 FROM users
11 WHERE student_id = p_studentid
12 AND password = md5 (p_currentp);
13
14 IF getcount = 1
15 THEN
16 UPDATE users
17 SET password = md5 (p_newpwd)
18 WHERE student_id = p_studentid
19 AND password = md5 (p_currentp);
20 END IF;
21
22 RETURN getcount;
23 END;
24 /
Function created.
SQL> SELECT updatepassword ('Overflow', 'Littlefoot', '100') FROM DUAL;
SELECT updatepassword ('Overflow', 'Littlefoot', '100') FROM DUAL
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.UPDATEPASSWORD", line 16
SQL>
Finally, a function which shows that you can do DML (but you really don't want to do that) - use pragma autonomous_transaction which instructs Oracle to execute code within that function separately from an outside transaction. It requires you to commit (or rollback).
SQL> CREATE OR REPLACE FUNCTION updatepassword (p_currentp IN VARCHAR2,
2 p_newpwd IN VARCHAR2,
3 p_studentid IN VARCHAR2)
4 RETURN NUMBER
5 IS
6 PRAGMA AUTONOMOUS_TRANSACTION;
7 getcount INTEGER := 0;
8 BEGIN
9 SELECT COUNT (*)
10 INTO getcount
11 FROM users
12 WHERE student_id = p_studentid
13 AND password = md5 (p_currentp);
14
15 IF getcount = 1
16 THEN
17 UPDATE users
18 SET password = md5 (p_newpwd)
19 WHERE student_id = p_studentid
20 AND password = md5 (p_currentp);
21 END IF;
22
23 COMMIT;
24
25 RETURN getcount;
26 END;
27 /
Function created.
SQL> SELECT updatepassword ('Overflow', 'Littlefoot', '100') FROM DUAL;
UPDATEPASSWORD('OVERFLOW','LITTLEFOOT','100')
---------------------------------------------
1
SQL> select * from users;
STUDENT_ID PASSWORD
---------- --------------------
100 Littlefoot
SQL>

Run query dynamically in stored procedure in Oracle

Select all the tables of database where column match than pass table name to next query using loop. If column name and column values matches than return true and exist for loop using a stored procedure:
CREATE OR REPLACE PROCEDURE TEST
(
NAME IN VARCHAR2 ,
ID IN NUMBER,
RE OUT SYS_REFCURSOR
) AS
BEGIN
OPEN RE FOR SELECT A.TABLE_NAME FROM
user_tables A JOIN user_tab_columns C
ON C.TABLE_NAME = A.TABLE_NAME
WHERE C.COLUMN_NAME = NAME;
FOR RE IN LOOP
v_Sql := 'SELECT COUNT(*) FROM '|| LOOP.TABLE_NAME || 'WHERE COLUMN_NAME =
ID';
EXECUTE IMMEDIATE v_Sql
IF v_Sql%ROWCOUNT > 0 THEN
return true;
EXIT
END LOOP;
END TEST;
For more understanding the problem
//Get all the tables of database where campus_id is exist in any table of
database
Campus, Class, Section (3 tables found)
Apply forloop on the records
Select count(campus_id) as total from (table name using loop) where campus_id = 1(value
pass)
if(total > 0){
Exist for loop and return true
}
else{
Again iterate the loop to next value
}
What you described doesn't make much sense. If there are several tables that contain a column you're checking and you exit the loop as soon as you find the first one, what about the rest of them?
Here's what I'd do, see if it helps. I'll create a function (not a procedure) that returns a table. In order to do that, I'll create type(s) first:
SQL> create or replace type t_record as object (tn varchar2(30), cnt number);
2 /
Type created.
SQL> create or replace type t_table as table of t_record;
2 /
Type created.
SQL>
The function:
in a cursor FOR loop I'm selecting tables that contain that column
L_STR is used to compose the SELECT statement
DBMS_OUTPUT.PUT_LINE is used to display it first, so that I could visually check whether it is correctly set or not.
if it is, I'm running it with the EXECUTE IMMEDIATE
the result is stored into a table type and returned to the caller
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return t_table
6 is
7 retval t_table := t_table();
8 l_str varchar2(200);
9 l_cnt number;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 retval.extend;
23 retval(retval.count) := t_record(cur_r.table_name, l_cnt);
24 end loop;
25 return retval;
26 end;
27 /
Function created.
Testing:
SQL> select * from table (f_colname('DEPTNO', '10'));
TN CNT
------------------------------ ----------
TEST_201812 1
DEPT 1
EMP 3
SQL> select * from table (f_colname('ENAME', 'KING'));
TN CNT
------------------------------ ----------
EMP 1
BONUS 1
SQL>
That won't work properly for some datatypes (such as DATE) and will have to be adjusted, if necessary.
[EDIT: after you edited the question]
OK then, that's even simpler. It should still be a function (that returns a Boolean, as you said that - in case that something's being found - you want to return TRUE). Code is pretty much similar to the previous function.
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return boolean
6 is
7 l_str varchar2(200);
8 l_cnt number;
9 retval boolean := false;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 if l_cnt > 0 then
23 retval := true;
24 exit;
25 end if;
26 end loop;
27 return retval;
28 end;
29 /
Function created.
Testing: as you can't return Boolean at SQL layer, you have to use an anonymous PL/SQL block, as follows:
SQL> declare
2 l_ret boolean;
3 begin
4 if f_colname('DEPTNO', '15') then
5 dbms_output.put_line('It exists');
6 else
7 dbms_output.put_line('It does not exist');
8 end if;
9 end;
10 /
It does not exist
PL/SQL procedure successfully completed.
SQL>

Wrap select with an Oracle function

In my mind, I'm writing a function such that calling something like
select get_foo() from dual;
or
select * from table (get_foo);
returns the same result as
select * from foo;
So, I've got a function that compiles...
create or replace function get_foo return sys_refcursor as
rc_foo sys_refcursor;
begin
open rc_foo for 'select * from foo';
return rc_foo;
end;
but select get_foo() from dual returns 1 row.
((ID=1,NAME=Sarah1),(ID=2,NAME=Sarah2),(ID=3,NAME=Sarah3),)
whilst select * from table( get_foo() ) gives me ORA-22905.
How do I change the function definition and/or the call to get the desired outcome?
you use a pipelined function.
for example:
SQL> create table foo(id , name) as select rownum, 'Sarah'||rownum from dual connect by level <= 3;
Table created.
SQL> create or replace package pipeline_test
2 as
3 type foo_tab is table of foo%rowtype;
4 function get_foo
5 return foo_tab PIPELINED;
6 end;
7 /
Package created.
SQL> create or replace package body pipeline_test
2 as
3 function get_foo
4 return foo_tab PIPELINED
5 is
6 v_rc sys_refcursor;
7 t_foo foo_tab;
8
9 begin
10 open v_rc for select * from foo;
11 loop
12 fetch v_rc bulk collect into t_foo limit 100;
13 exit when t_foo.count = 0;
14 for idx in 1..t_foo.count
15 loop
16 pipe row(t_foo(idx));
17 end loop;
18 end loop;
19 end;
20 end;
21 /
Package body created.
SQL> select * from table(pipeline_test.get_foo());
ID NAME
---------- ---------------------------------------------
1 Sarah1
2 Sarah2
3 Sarah3

Resources