Oracle SQL procedure getting invalid after table alter - oracle

When trying to alter a table , one of the procedure which has only select statement is getting invalid.
table scripts:
create table t1(a number,b number);
create or replace procedure p1
is
x number;
y number;
begin
select a,b into x,y from t1;
end;
/
create or replace procedure p2(i number)
is
x number;
y number;
begin
select a,b into x,y from t1 where i=1;
end;
/
alter table t1 add (d number);
select object_name,status from dba_objects where object_name in ('T','P1','P2');
T VALID
P1 VALID
P2 INVALID
observed that when procedure takes in parameter and if we are using it in the select statement, the object is getting invalid or else it is not getting invalid.
Is it possible to alter the table without the procedure getting invalid?

Actually, your object is not really invalid. The issue happens as your second procedure contains a where clause that produces an invalidation in the dictionary based on rules.
If you run the procedure, it will become valid automatically.
Demo
Oracle 19c
SQL> alter session set current_schema=test1 ;
Session altered.
SQL> create table t1(a number,b number);
Table created.
SQL> create or replace procedure p1
is
x number;
y number;
begin
select a,b into x,y from t1;
end;
/ 2 3 4 5 6 7 8
Procedure created.
SQL> create or replace procedure p2(i number)
is
x number;
y number;
begin
select a,b into x,y from t1 where i=1;
end;
/
2 3 4 5 6 7 8
Procedure created.
SQL> alter table t1 add (d number);
Table altered.
SQL> select object_name,status from dba_objects where object_name in ('T1','P1','P2') and owner='TEST1' ;
OBJECT_NAME STATUS
------------------------- --------------------
P1 VALID
P2 INVALID
T1 VALID
Now, if the procedure was really invalid, you could not execute it. Let's insert one row in your table and try to run it.
SQL> insert into t1 values ( 1 ,1 ,1) ;
1 row created.
SQL> commit ;
Commit complete.
SQL> set serveroutput on
SQL> exec p2 ( i=>1 ) ;
PL/SQL procedure successfully completed.
SQL> select object_name,status from dba_objects where object_name in ('T','P1','P2') and owner='TEST1' ;
OBJECT_NAME STATUS
------------------------- --------------------
P1 VALID
P2 VALID
T1 VALID
There are internal rules which define the invalidation of objects in the dictionary, such as procedures, functions or packages when some changes happen in the objects which those have dependencies upon.
In this specific case, there is no real invalidation, as the object was only marked as invalid in the dictionary after the DDL was executed.
The reason why the first one was not invalid is because it does not contain any where clause, and only is retrieving the two existing columns. The second becomes invalid as it contains a where clause to a PLSQL variable. See below
https://docs.oracle.com/cd/E24693_01/appdev.11203/e17125/adfns_dependencies.htm#CHDJIIFC

Related

Oracle: create stored procedure to insert results from another stored procedure into a table

I have an Oracle stored procedure spFinTest that I use in an SSRS report.
I wish to create another stored procedure spFinTestInsert that takes the output of spFinTest and inserts it into a table sbceybudget_financial_year.
I can create an all in one stored procedure that inserts the data into the destination table, but what I am looking to achieve is to just have the one stored procedure that can have code updates for data extracts and not to have to update a second separate insert stored procedure that has the same code.
So, update in one place only and reuse the same stored procedure.
The following is a simplified version of the main stored procedure:
create or replace procedure spFINTEST
(s1 OUT SYS_REFCURSOR)
AS
BEGIN
OPEN s1 FOR
SELECT
FIN_YR + 1 AS FIN_YR,
SCHOOL_YEAR_WEEKS
FROM
sbceybudget_financial_year
WHERE
fin_yr = 2021
;
END spFINTEST;
This stored procedure only has an "out" variable.
The final intention is once I can do this, then I will call spFinTestInsert from an "Execute SQL Task" in an SSIS package.
I'm a bit stumped as to how I create this second stored procedure that calls the first and inserts the results into a named table, so if anyone can help I would be most grateful.
I would do this in a package: it allows you to declare cursor type easily to make it more clear.
Test table:
create table sbceybudget_financial_year(
fin_yr int,
SCHOOL_YEAR_WEEKS int
)
/
Package specification:
create or replace package pkg_spFIN as
type spFIN_RowType is record(
FIN_YR sbceybudget_financial_year.FIN_YR%type,
SCHOOL_YEAR_WEEKS sbceybudget_financial_year.SCHOOL_YEAR_WEEKS%type
);
type spFIN_CurType IS REF CURSOR RETURN spFIN_RowType;
type spFIN_tab is table of spFIN_RowType;
procedure spFINTEST (s1 OUT SYS_REFCURSOR);
procedure spFinTestInsert;
end pkg_spFIN;
/
Package body:
create or replace package body pkg_spFIN as
function get_cursor(n int) return spFIN_CurType is
c spFIN_CurType;
begin
open c for
SELECT
t.FIN_YR + 1 AS FIN_YR,
t.SCHOOL_YEAR_WEEKS
FROM
sbceybudget_financial_year t
WHERE
t.fin_yr = n;
return c;
end;
procedure spFINTEST (s1 OUT SYS_REFCURSOR)
is
begin
s1:=get_cursor(2021);
end spFINTEST;
procedure spFinTestInsert
is
cur spFIN_CurType;
tab spFIN_tab;
begin
pkg_spFIN.spFINTEST(cur);
loop
fetch cur bulk collect into tab limit 100;
exit when tab.count()=0;
for i in 1..tab.count loop
dbms_output.put_line(tab(i).FIN_YR);
dbms_output.put_line(tab(i).SCHOOL_YEAR_WEEKS);
-- or insert:
-- insert into sbceybudget_financial_year(fin_yr, SCHOOL_YEAR_WEEKS)
-- values(tab(i).FIN_YR, SCHOOL_YEAR_WEEKS)
-- you can change it to FORALL insert
end loop;
end loop;
end spFinTestInsert;
end pkg_spFIN;
/
Test data:
begin
insert into sbceybudget_financial_year(fin_yr, SCHOOL_YEAR_WEEKS) values(2019,19);
insert into sbceybudget_financial_year(fin_yr, SCHOOL_YEAR_WEEKS) values(2020,20);
insert into sbceybudget_financial_year(fin_yr, SCHOOL_YEAR_WEEKS) values(2021,21);
commit;
end;
/
And finally test call:
call pkg_spFIN.spFinTestInsert();
Full example on DBFiddle: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=c31a5714e5db74eaa3a83fae03964349
I don't have your tables so I'll use Scott's DEPT for illustration.
This is the "target" table; values fetched by ref cursor will be inserted into it:
SQL> create table test_dept as select deptno, dname from dept where 1 = 2;
Table created.
This is data I expect:
SQL> select deptno, dname from dept where deptno <= 20;
DEPTNO DNAME
---------- --------------
10 ACCOUNTING
20 RESEARCH
This is your current procedure:
SQL> create or replace procedure spfintest (s1 out sys_refcursor)
2 as
3 begin
4 open s1 for select deptno, dname from dept where deptno <= 20;
5 end spfintest;
6 /
Procedure created.
This is a procedure which calls spfintest and inserts values into test_dept:
SQL> create or replace procedure spfitestinsert as
2 rc sys_refcursor;
3 --
4 l_deptno dept.deptno%type;
5 l_dname dept.dname%type;
6 begin
7 spfintest(rc);
8 loop
9 fetch rc into l_deptno, l_dname;
10 exit when rc%notfound;
11
12 insert into test_dept (deptno, dname)
13 values (l_deptno, l_dname);
14 end loop;
15 end;
16 /
Procedure created.
Testing:
SQL> exec spfitestinsert;
PL/SQL procedure successfully completed.
SQL> select * from test_dept;
DEPTNO DNAME
---------- --------------
10 ACCOUNTING
20 RESEARCH
SQL>
Everything is here, so I guess it works.

In Oracle SQL, I would like to call a Oracle stored procedure and then select the value of an OUT parameter as a column result. Is this possible?

CREATE OR REPLACE PROCEDURE myStoredProcedure (idParam IN VARCHAR2,
outputParam OUT VARCHAR2)
AS
BEGIN
SELECT OUTPUTCOL INTO outputParam FROM MyTable WHERE ID = idParam;
END;
DECLARE
v_OutputResults VARCHAR2(20);
BEGIN
myStoredProcedure('123', v_OutputResults);
SELECT v_OutputResults AS ColumnResult FROM DUAL;
END;
If we understand your goal, you should be using a function, not a procedure:
First, we set up the example:
SQL> -- conn system/halftrack#pdb01
SQL> conn scott/tiger#pdb01
Connected.
SQL> --
SQL> CREATE TABLE my_table (
2 user_id number not null,
3 Name varchar2(10)
4 )
5 ;
Table created.
SQL> --
SQL> insert into my_table values (1,'Bob');
1 row created.
SQL> insert into my_table values (2,'Carol');
1 row created.
SQL> insert into my_table values (3,'Ted');
1 row created.
SQL> insert into my_table values (4,'Alice');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from my_table;
USER_ID NAME
---------- ----------
1 Bob
2 Carol
3 Ted
4 Alice
4 rows selected.
Now we create the function, then use it:
SQL> --
SQL> create or replace function my_function (id_param number)
2 return varchar2
3 is
4 v_name varchar2(10);
5 begin
6 select name
7 into v_name
8 from my_table
9 where user_id = id_param
10 ;
11 --
12 return v_name;
13 end;
14 /
Function created.
SQL> show errors
No errors.
SQL> --
SQL> select my_function(1) from dual;
MY_FUNCTION(1)
--------------------------------------------------------------------------------
Bob
1 row selected.
And clean up our example:
SQL> --
SQL> drop table my_table purge;
Table dropped.
No but you can do so using a stored function.

Procedures giving error : ORA-00955: name is already used by an existing object

CREATE TABLE product
( product_id number(10) NOT NULL,
product_name varchar2(50) NOT NULL,
price varchar2(50)
);
CREATE OR REPLACE PROCEDURE p1 (product_id in number,
product_name in varchar2,
price in number)
IS
BEGIN
INSERT INTO product values(1,'yash',100);
DBMS_OUTPUT.PUT_LINE('VALUE INSERTED');
end;
Error: ORA-00955: name is already used by an existing object
Can someone please provide me and suitable example
From the error message, the name p1 is already in use (as an object name whose type is different than a procedure). Just give the procedure a different (and more descriptive!) name:
CREATE OR REPLACE PROCEDURE insert_product
(product_id in number,
product_name in varchar2,
price in number)
IS
BEGIN
INSERT INTO product values(1,'yash',100);
DBMS_OUTPUT.PUT_LINE('VALUE INSERTED');
END;
For your this problem the solution is write the following line (EXECUTE p1;)
By typing this command you will get your desired output
You have twice asked for a 'suitable example' or 'justifying code'. I assume from that you don't understand or don't believe what has been explained about your procedure name conflicting with some other object name. So here's the example that proves it.
SQL> --
SQL> -- check that we don't own anything called MYTEST
SQL> --
SQL> select object_name
2 from user_objects
3 where upper(object_name) = 'MYTEST'
4 ;
no rows selected
SQL> --
SQL> -- create a table MYTEST
SQL> --
SQL> create table mytest (dob date);
Table created.
SQL> --
SQL> -- check objets
SQL> --
SQL> select object_type,
2 object_name
3 from user_objects
4 where upper(object_name) = 'MYTEST'
5 ;
OBJECT_TYPE OBJECT_NAME
------------------- --------------------
TABLE MYTEST
1 row selected.
SQL> --
SQL> -- create a procedure MYTEST
SQL> --
SQL> create or replace procedure mytest
2 as
3 begin
4 dbms_output.put_line('Hello world');
5 end;
6 /
create or replace procedure mytest
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> --
SQL> -- now, drop the table and try the procedure again
SQL> --
SQL> drop table mytest purge;
Table dropped.
SQL> create or replace procedure mytest
2 as
3 begin
4 dbms_output.put_line('Hello world');
5 end;
6 /
Procedure created.
SQL> --
SQL> -- cleanup
SQL> --
SQL> drop table mytest purge;
drop table mytest purge
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> drop procedure mytest;
Procedure dropped.
In your case if your procedure is named 'p1' then check:
select object_type,
object_name
from user_objects
where upper(object_name) = 'P1'
;

Unable to find procedure in DBA_PROCEDURES view

I have created a procedure :
create or replace procedure gg as
begin
insert into book values ('prashant','prashant','prashant');
commit;
end;
/
Procedure has been created successfully.Now i want to check the package name for the corresponding procedure but i am not able to do so.
I am using the below query :
> SELECT *
FROM SYS.DBA_PROCEDURES
WHERE procedure_name ='gg';
Its giving 0 rows selected.Please help.
NOTE Please look at the UPDATE section for correct answer.
The procedure name cannot be in lower case in the DBA_PROCEDURES view. Use upper case, or apply UPPER function.
SELECT *
FROM SYS.DBA_PROCEDURES
WHERE procedure_name ='GG';
UPDATE
The only case when you could have the name in lower case is if you enclose it within double-quotation marks while compiling.
For example,
SQL> CREATE OR REPLACE
2 PROCEDURE "p"
3 AS
4 BEGIN
5 NULL;
6 END;
7 /
Procedure created.
SQL> SELECT object_name, procedure_name, object_type FROM user_procedures where procedure_name='p';
no rows selected
SQL>
But still the above view will not return any result for PROCEDURE_NAME.
Reason
PROCEDURE_NAME column will only have the procedure name for the procedures which are part of a PACKAGE. For STAND ALONE PROCEDURES you need to use OBJECT_NAME.
SQL> -- stand alone procedure in lower case
SQL> CREATE OR REPLACE
2 PROCEDURE "p"
3 AS
4 BEGIN
5 NULL;
6 END;
7 /
Procedure created.
SQL>
SQL> -- package
SQL> CREATE OR REPLACE
2 PACKAGE test_p
3 IS
4 PROCEDURE p;
5 END test_p;
6 /
Package created.
SQL>
SQL> -- package body with a procedure
SQL> CREATE OR REPLACE
2 PACKAGE BODY test_p
3 IS
4 PROCEDURE p
5 IS
6 BEGIN
7 NULL;
8 END;
9 END test_p;
10 /
Package body created.
SQL>
SQL> SELECT object_name, procedure_name, object_type FROM user_procedures;
OBJECT_NAME PROCEDURE_NAME OBJECT_TYPE
--------------- --------------- ---------------
TEST_P P PACKAGE
p PROCEDURE
TEST_P PACKAGE
SQL>
So, as you can see, the procedure_name is only having the package's procedure, however the stand-alone procedure is only listed under object_name.
The query I use is:
SELECT * FROM User_Procedures WHERE NVL(Procedure_Name,Object_Name) = 'PROCNAME';

Passing an array of strings to a procedure and using it in the WHERE clause with IN

I want to pass a list of strings to a procedure and use it in the WHERE clause of a select statement, but I can't figure out how to do this.
I have declared the following nested table:
TYPE t_strarray IS TABLE OF VARCHAR2(30);
The procedure looks as follows:
PROCEDURE getstuff(p_list IN t_strarray, io_cursor OUT t_cursor)
IS
BEGIN
OPEN io_cursor FOR
SELECT * FROM mytable
WHERE mytable.field1 in (select * from table(p_list));
END;
How can this be done?
Your array needs to be a SQL object type, created directly in SQL, not a PLSQL type declared in a package:
SQL> CREATE OR REPLACE TYPE t_strarray IS TABLE OF VARCHAR2(30);
2 /
Type created.
SQL> CREATE TABLE mytable (field1 VARCHAR2(30));
Table created.
SQL> INSERT INTO mytable VALUES ('A');
1 row created.
SQL> INSERT INTO mytable VALUES ('D');
1 row created.
SQL> CREATE OR REPLACE PROCEDURE getstuff(p_list IN t_strarray,
2 io_cursor OUT SYS_REFCURSOR) IS
3 BEGIN
4 OPEN io_cursor FOR
5 SELECT *
6 FROM mytable
7 WHERE mytable.field1 IN (SELECT COLUMN_VALUE FROM TABLE(p_list));
8 END;
9 /
Procedure created.
SQL> VARIABLE cc REFCURSOR;
SQL> EXEC getstuff (t_strarray('A', 'B', 'C'), :cc);
PL/SQL procedure successfully completed.
SQL> print cc
FIELD1
------------------------------
A

Resources