PL/SQL procedure with cursor and rowtype - oracle

I am working in a games data base. I want to create a procedure which shows the games created between two dates.
I am using a cursor and a rowtype like this:
CREATE OR REPLACE procedure p_games(v_date1 games.date%type, v_date2 games.date%type)
AS
v_games games%rowtype;
CURSOR checkGames IS
SELECT * INTO v_games
FROM games
WHERE date BETWEEN v_date1 AND v_date2;
BEGIN
FOR register IN checkGames LOOP
dbms_output.put_line(register.v_games);
END LOOP;
END;
/
but when I run it the error is
PLS-00302: the component 'V_GAMES' must be declared.
Should I declare it in any other way?

Not exactly like that.
you don't have to declare cursor variable as you're using a cursor FOR loop
you don't select INTO while declaring a cursor; you would FETCH into if you used a different approach (see example below)
Sample table:
SQL> create table games
2 (id number,
3 c_date date
4 );
Table created.
SQL> insert into games (id, c_date) values (1, date '2022-04-25');
1 row created.
Your procedure, slightly modified:
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 BEGIN
9 FOR register IN checkGames LOOP
10 dbms_output.put_line(register.id);
11 END LOOP;
12 END;
13 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>
A different approach; as you can notice, a cursor FOR loop is way simpler as Oracle does most of the dirty job for you (opening the cursor, fetching from it, taking care about exiting the loop, closing the cursor):
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 v_games checkGames%rowtype;
9 BEGIN
10 open checkGames;
11 loop
12 fetch checkGames into v_games;
13 exit when checkGames%notfound;
14
15 dbms_output.put_line(v_games.id);
16 END LOOP;
17 close checkGames;
18 END;
19 /
Procedure created.
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>

Related

SQL statement ignored and missing opening parenthesis

I get these errors during a creation of a trigger but I don't understand why
create or replace NONEDITIONABLE TRIGGER magazzino_bef_ins
BEFORE INSERT ON MAGAZZINO
FOR EACH ROW
DECLARE
tempcodice varchar2(8);
cacc char(3);
ogg int;
CURSOR cursore_disp IS
SELECT cacciatore, oggetto
FROM TABLE EDDY.disponibilita
WHERE ID_DISPONIBILITA = :NEW.disponibilità;
BEGIN
CURSOR cursore_disp IS
SELECT cacciatore, oggetto
FROM TABLE DISPONIBILITA
WHERE ID_DISPONIBILITA = :NEW.disponibilità;
open cursore_disp;
fetch cursore_disp into cacc,ogg;
temp:= pk_gestione_magazzino.genera_catalogo(cacc,ogg);
:new.codcatalogo:=temp;
END;
The errors are:
Error(5,9): PL/SQL: SQL Statement ignored
Errore(6,20): PL/SQL: ORA-00906: missing opening parenthesis
Errore(9,8): PLS-00103: encountered symbol "CURSORE_DISP" instead of one of the following: := . ( # % ;
I can't understand these errors, I'm just trying to take the values from a table in where the id inserted is equal in the other table.
Here's how. I created test environment which is kind of stupid, but it makes the following code work.
SQL> create table magazzino (disponibilita number, codcatalogo varchar2(8));
Table created.
SQL> create table disponibilita (cacciatore char(3), oggetto number, id_disponibilita number);
Table created.
SQL> create or replace package pk_gestione_magazzino as
2 function genera_catalogo (cacc number, ogg number) return varchar2;
3 end;
4 /
Package created.
SQL> create or replace package body pk_Gestione_magazzino as
2 function genera_catalogo (cacc number, ogg number) return varchar2 is
3 begin
4 return 'Little';
5 end;
6 end;
7 /
Package body created.
Trigger which causes you trouble: no need for a cursor; moreover, you have two of them (was it just a typo)? It looks as if you used it to avoid no data found error. If so, don't do it - it just makes confusion. Handle exceptions properly. By the way, you "forgot" to close the cursor.
SQL> create or replace trigger magazzino_bef_ins
2 before insert on magazzino
3 for each row
4 declare
5 cacc char(3);
6 ogg int;
7 begin
8 select cacciatore, oggetto
9 into cacc, ogg
10 from disponibilita
11 where id_disponibilita = :new.disponibilita;
12
13 :new.codcatalogo := pk_gestione_magazzino.genera_catalogo (cacc, ogg);
14 end;
15 /
Trigger created.
Testing:
SQL> insert into disponibilita values (1, 1, 1);
1 row created.
SQL> insert into magazzino (disponibilita) values (1);
1 row created.
SQL> select * From magazzino;
DISPONIBILITA CODCATAL
------------- --------
1 Little
SQL>
Works, kind of.

How to pick a particular package based on a particular table column value in Oracle?

I have a situation here:
We need to run procedure_1 in a package when a particular column say column X has data in it and simultaneously run another procedure_2 in package if there is no data in that column X
Can anyone please advice on what can be done using Oracle ?
Something like this?
table DECIDE contains PARTICULAR_COLUMN
package has two simple procedures which do nothing; they just identify themselves
.
SQL> create table decide
2 (id number,
3 particular_column varchar2(1));
Table created.
SQL> insert into decide values (1, 'X');
1 row created.
SQL> insert into decide values (2, null);
1 row created.
SQL> create or replace package pkg_decide as
2 procedure p1;
3 procedure p2;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_decide as
2 procedure p1 is
3 begin
4 dbms_output.put_line('-> running proc 1');
5 end;
6
7 procedure p2 is
8 begin
9 dbms_output.put_line('-> running proc 2');
10 end;
11 end;
12 /
Package body created.
SQL>
Testing: if the PARTICULAR_COLUMN isn't empty, P1 will run; otherwise, P2. That can be done with dynamic SQL, i.e. EXECUTE IMMEDIATE. As we're going to run a procedure, we have to form a full anonymous PL/SQL block (begin - procedure name - end).
P.S. As suggested by Matthew, a simple IF will do.
SQL> begin
2 for cur_r in (select particular_column,
3 case when particular_column is not null then 'pkg_decide.p1'
4 else 'pkg_decide.p2'
5 end prc_name
6 from decide
7 ) loop
8 dbms_output.put_line('particular column = ' || cur_r.particular_column ||
9 ', should run procedure ' || cur_r.prc_name);
10 execute immediate 'begin ' ||cur_r.prc_name ||'; end;';
11
12 -- Or, as suggested by Matthew, a simple IF will do
13 if cur_r.particular_column is not null then
14 pkg_decide.p1;
15 else
16 pkg_decide.p2;
17 end if;
18 end loop;
19 end;
20 /
particular column = X, should run procedure pkg_decide.p1
-> running proc 1
-> running proc 1
particular column = , should run procedure pkg_decide.p2
-> running proc 2
-> running proc 2
PL/SQL procedure successfully completed.
SQL>

plsql procedures while executing an error invalid identifier 00904

Error message
CREATE TABLE DEPARTMENT(DID INT,DNAME VARCHAR2(20),DLOC VARCHAR2(20));
INSERT INTO DEPARTMENT VALUES(101,'SRINATH','HINDUPUR');
INSERT INTO DEPARTMENT VALUES(102,'SAINATH','ANANTAPUR');
create or replace PROCEDURE ADD_DEPARTMENT
(P_DID IN DEPARTMENT.DID%TYPE,
P_DNAME IN DEPARTMENT.DNAME%TYPE,
P_DLOC IN DEPARTMENT.DLOC%TYPE,
P_ERROR_MSG OUT VARCHAR2)
IS
BEGIN
INSERT INTO DEPARTMENT(DID,DNAME,DLOC)VALUES(P_DID,P_DNAME,P_DLOC);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
P_ERROR_MSG:=SQLERRM;
END ADD_DEPARTMENT;
iam writing a simple procedure try to excute but it shows object_id:invalid identifier how i can solve
complete procedure execution image
One of your column names may be wrong in the parameter list/insert. Try just using varchar2 for all of them and see if that helps
create or replace PROCEDURE ADD_DEPT2
(P_DEPTNO IN VARCHAR2,
P_DNAME IN VARCHAR2,
P_LOC IN VARCHAR2,
P_ERROR_MSG OUT VARCHAR2)
IS
BEGIN
INSERT INTO DEPT1(DEPTNO,DNAME,LOC)VALUES(P_DEPTNO,P_DNAME,P_LOC);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
P_ERROR_MSG :=SQLERRM;
END ADD_DEPT2;
"it will come same error can be displayed"
No, no it won't. Let's prove it by running the code you posted.
Here is your table:
SQL> CREATE TABLE DEPARTMENT(DID INT,DNAME VARCHAR2(20),DLOC VARCHAR2(20));
Table created.
SQL> INSERT INTO DEPARTMENT VALUES(101,'SRINATH','HINDUPUR');
1 row created.
SQL> INSERT INTO DEPARTMENT VALUES(102,'SAINATH','ANANTAPUR');
1 row created.
SQL>
Here is your procedure:
SQL> create or replace PROCEDURE ADD_DEPARTMENT
2 (P_DID IN DEPARTMENT.DID%TYPE,
3 P_DNAME IN DEPARTMENT.DNAME%TYPE,
4 P_DLOC IN DEPARTMENT.DLOC%TYPE,
5 P_ERROR_MSG OUT VARCHAR2)
6 IS
7 BEGIN
8 INSERT INTO DEPARTMENT(DID,DNAME,DLOC)VALUES(P_DID,P_DNAME,P_DLOC);
9 COMMIT;
10 EXCEPTION
11 WHEN OTHERS THEN
12 P_ERROR_MSG:=SQLERRM;
13 END ADD_DEPARTMENT;
14 /
Procedure created.
SQL>
And lo! not only does it compile but it runs without error too.
SQL> set serveroutput on
SQL> declare
2 v varchar2(128);
3 begin
4 add_department(666, 'Area 51', 'Roswell', v);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select * from department;
DID DNAME DLOC
---------- -------------------- --------------------
101 SRINATH HINDUPUR
102 SAINATH ANANTAPUR
666 Area 51 Roswell
SQL>
So once again I ask, what is the problem?
"PLS-00905: object SCOTT.ADD_DEPARTMENT is invalid"
In SQL*Plus:
Connect as SCOTT
alter procedure add_department compile;
show errors
Or apply the equivalent sequence for whatever IDE you use to write your code.

Using cursor with where in condition

I am passing arguments `EBN,BGE' into a procedure , then I am passing this argument to a cursor.
create or replace procedure TEXT_MD (AS_IDS VARCHAR2)
is
CURSOR C_A (AS_ID VARCHAR2) IS
SELECT
name
FROM S_US
WHERE US_ID IN (AS_ID);
BEGIN
FOR A IN C_A (AS_IDS) LOOP
DBMS_OUTPUT.PUT_LINE('I got here: '||AS_IDS);
end loop;
END;
But while debuging the count of the cursor is still null
So my question , why the cursor not returning values with in condition
You are passing a string parameter, so it will be used as a string, not as a list of strings; so, your cursor will be something like
SELECT name
FROM S_US
WHERE US_ID IN ('EBN,BGE')
This will, of course, not do what you need.
You may need to change your procedure and the way to pass parameters; if you want to keep a string parameter , one way could be the following:
setup:
SQL> CREATE TABLE S_US
2 (
3 US_ID,
4 NAME
5 ) AS
6 SELECT 'EBN', 'EBN name' FROM DUAL
7 UNION ALL
8 SELECT 'BGE', 'BGE name' FROM DUAL;
Table created.
procedure:
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_2(AS_IDS VARCHAR2) IS
2 vSQL varchar2(1000);
3 c sys_refcursor;
4 vName varchar2(16);
5 BEGIN
6 vSQL := 'SELECT name
7 FROM S_US
8 WHERE US_ID IN (' || AS_IDS || ')';
9 open c for vSQL;
10 loop
11 fetch c into vName;
12 if c%NOTFOUND then
13 exit;
14 end if;
15 DBMS_OUTPUT.PUT_LINE(vName);
16 END LOOP;
17 END;
18 /
Procedure created.
You need to call it with a string already formatted to be a parameter list for IN:
SQL> EXEC TEXT_MD_2('''EBN'',''BGE''');
EBN name
BGE name
PL/SQL procedure successfully completed.
This is only an example of a possible way, and not the way I would do this.
Among the reasons to avoud this kind of approach, consider what Justin Cave says:
"that would be a security risk due to SQL injection and would have a potentially significant performance penalty due to constant hard parsing".
I believe you should better check how to pass a list of values to your procedure, rather then using a string to represent a list of strings.
Here is a possible way to do the same thing with a collection:
SQL> CREATE OR REPLACE TYPE tabVarchar2 AS TABLE OF VARCHAR2(16)
2 /
Type created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_3(AS_IDS tabVarchar2) IS
2 vSQL VARCHAR2(1000);
3 c SYS_REFCURSOR;
4 vName VARCHAR2(16);
5 BEGIN
6 FOR i IN (SELECT name
7 FROM S_US INNER JOIN TABLE(AS_IDS) tab ON (tab.COLUMN_VALUE = US_ID))
8 LOOP
9 DBMS_OUTPUT.PUT_LINE(i.name);
10 END LOOP;
11 END;
12 /
Procedure created.
SQL>
SQL> DECLARE
2 vList tabVarchar2 := NEW tabVarchar2();
3 BEGIN
4 vList.EXTEND(2);
5 vList(1) := 'BGE';
6 vList(2) := 'EBN';
7 TEXT_MD_3(vList);
8 END;
9 /
BGE name
EBN name
PL/SQL procedure successfully completed.
SQL>
Again, you can define collections in different ways, within a stored procedure or not, indexed or not, and so on; this is only one of the possible ways, not necessarily the best, depending on your environment, needs.

Get the name of the calling procedure or function in Oracle PL/SQL

Does anyone know whether it's possible for a PL/SQL procedure (an error-logging one in this case) to get the name of the function/procedure which called it?
Obviously I could pass the name in as a parameter, but it'd be nice to make a system call or something to get the info - it could just return null or something if it wasn't called from a procedure/function.
If there's no method for this that's fine - just curious if it's possible (searches yield nothing).
There is a package called OWA_UTIL (which is not installed by default in older versions of the database). This has a method WHO_CALLED_ME() which returns the OWNER, OBJECT_NAME, LINE_NO and CALLER_TYPE. Note that if the caller is a packaged procedure it will return the PACKAGE name not the procedure name. In this case there is no way of getting the procedure name; this is because the procedure name can be overloaded, so it's not necessarily very useful.
Find out more.
Since 10gR2 there is also the $$PLSQL_UNIT special function; this will also return the OBJECT NAME (i.e. package not packaged procedure).
I found this forum: http://www.orafaq.com/forum/t/60583/0/. It may be what you are looking.
Basically, you can use the Oracle supplied dbms_utility.format_call_stack:
scott#ORA92> CREATE TABLE error_tab
2 (who_am_i VARCHAR2(61),
3 who_called_me VARCHAR2(61),
4 call_stack CLOB)
5 /
Table created.
scott#ORA92>
scott#ORA92> CREATE OR REPLACE PROCEDURE d
2 AS
3 v_num NUMBER;
4 v_owner VARCHAR2(30);
5 v_name VARCHAR2(30);
6 v_line NUMBER;
7 v_caller_t VARCHAR2(100);
8 BEGIN
9 select to_number('a') into v_num from dual; -- cause error for testing
10 EXCEPTION
11 WHEN OTHERS THEN
12 who_called_me (v_owner, v_name, v_line, v_caller_t);
13 INSERT INTO error_tab
14 VALUES (who_am_i,
15 v_owner || '.' || v_name,
16 dbms_utility.format_call_stack);
17 END d;
18 /
Procedure created.
scott#ORA92> SHOW ERRORS
No errors.
scott#ORA92> CREATE OR REPLACE PROCEDURE c
2 AS
3 BEGIN
4 d;
5 END c;
6 /
Procedure created.
scott#ORA92> CREATE OR REPLACE PROCEDURE b
2 AS
3 BEGIN
4 c;
5 END b;
6 /
Procedure created.
scott#ORA92> CREATE OR REPLACE PROCEDURE a
2 AS
3 BEGIN
4 b;
5 END a;
6 /
Procedure created.
scott#ORA92> execute a
PL/SQL procedure successfully completed.
scott#ORA92> COLUMN who_am_i FORMAT A13
scott#ORA92> COLUMN who_called_me FORMAT A13
scott#ORA92> COLUMN call_stack FORMAT A45
scott#ORA92> SELECT * FROM error_tab
2 /
WHO_AM_I WHO_CALLED_ME CALL_STACK
------------- ------------- ---------------------------------------------
SCOTT.D SCOTT.C ----- PL/SQL Call Stack -----
object line object
handle number name
6623F488 1 anonymous block
66292138 13 procedure SCOTT.D
66299430 4 procedure SCOTT.C
6623D2F8 4 procedure SCOTT.B
6624F994 4 procedure SCOTT.A
66299984 1 anonymous block
scott#ORA92>
Basically, all you need to do is to define vars and pass them in a call to a utility method to fill them up with values:
create or replace procedure some_test_proc (p_some_int int)
is
owner_name VARCHAR2 (100);
caller_name VARCHAR2 (100);
line_number NUMBER;
caller_type VARCHAR2 (100);
begin
....
OWA_UTIL.WHO_CALLED_ME (owner_name,caller_name,line_number,caller_type);
-- now you can insert those values along with systimestamp into a log file
....
end;

Resources