Read a file inside a package procedure in pl sql - sql

I am trying to read a text file inside a Pl Sql procedure but to no avail -- syntax errors it seems. What am I doing wrong? My suspicion is I am not declaring something where I should be. Here is the first path of the package body:
CREATE OR REPLACE PACKAGE BODY COP_DBO.PACKAGE_TEMPLATE
AS
--
--*****************************************************************************************************
-- Purpose: Just a template
--
-- Inputs:
-- in_vSTR String
--
-- Returns:
-- None
--
-- Mod History:
-- 06/29/2016 KEvin Palmer - Created initial version of this procedure
--
-- Error Handling:
-- An error is raised if errors are encountered building or executing the SQL.
--
--*****************************************************************************************************
f UTL_FILE.FILE_TYPE;
s VACHAR2(200);
BEGIN
f := UTL_FILE.FOPEN('\\sp0034avrt\winixdb$\cow\dev', 'certs_file.txt', 'R');
UTL_FILE.GET_LINE(f,s);
UTL_FILE.FLCOSE(f);
dbms_outpit.put_line(s);
end;
sql_statments arr_sql_t := arr_sql_t(); --initialize a empty lis
--------------------------------------------------------------------------------
/* PROCEDURE AND VARIABLE
INITILIZATION FOR COW_DATALOAD_V2
/***************************************************************************/
------------------------------------------------------------------------------
--*********** PUT YOUR LIST OF CERTS BELOW ******************
v_certList arr_claims_t := arr_claims_t('3803617642',
'3805126441',
'3876849047',
'3873116383',
'3873306670',
'3878876718');
--COP VARIABLES---
new_copId NUMBER; --NEW COP ID
prod_copId NUMBER; --PROD COP ID
new_seq_id NUMBER; --NEW SEQ ID
suppl_count NUMBER; --supplemental count
v_SQL VARCHAR2(7000);
v_certLst VARCHAR2(2000);
n_success NUMBER := 0; --Count of success found
n_total NUMBER := 0; --Total Records proccessed
n_suppl NUMBER := 0; --Total Records proccessed
n_orders NUMBER := 0; --Total lmso orders downloaded
/*cop procedure*/
PROCEDURE COP_DATALOAD_V2(arr_claims arr_claims_t,
arr_sql arr_sql_t) AS
BEGIN
After that begin I have two procedures. Everything after end for the file stuff is highlighted with some type of syntactic error. What am I doing wrong?
EDIT: How is this question a duplicate of that othre question? I don't see it. Sorry if it's obvious. I don't see the similarity at all.

Typical package body declaration looks like this
create or replace package body package_name is
var_name number;
procedure proc_name is
begin
do_smth;
end;
begin
init_smth;
end ;
Variables, methods, initialization. In your code as opposite:
create or replace package body package_name is
var_name number;
begin
init_smth;
end ;
var_name2 number;
procedure proc_name is
begin
...
Variables, initialization, variables again, methods. It looks like you should place pieces of code in right places

Related

Using plsql, issue authentication number and check if it matches the input value

Use plsql.
If you receive your name, resident registration number, and phone number when you register as a member, you will generate 6 digits of the authentication code.
Enter 6 digits of the authentication code and pass if it matches the received authentication code.
I want to implement this, but there's an error.
Is there any way to handle it in one procedure? (It doesn't matter if there are multiple procedures. )
That's what I've done so far.
This is a procedure to verify that the authentication number matches.
create or replace procedure auth_check (
authen_send number, --receive code
authen_input number --input code
,authen_check out number --check
)
is
begin
if authen_send=authen_input
then authen_check := 1;
dbms_output.put_line('success');
else
authen_check := 0;
dbms_output.put_line('fail');
end if;
commit;
end;
This generates the authentication number at random.
var aucode number;
begin
select substr(ltrim( ltrim(dbms_random.value, '0.'),'0'),0,6)
into :aucode from dual;
end;
print aucode;
var inputcode number;
var check number;
exec auth_check(:aucode,&inputcode,:check);
Well, it is a bit confusing, but maybe it is an authentication system where you generate the number -> send it to the users (SMS maybe) -> ask to enter the number -> and check it... maybe this could help
-- Procedure
--
CREATE OR REPLACE PROCEDURE
AUTH_CHECK( authen_send IN number, --receive code
authen_input IN number, --input code
authen_check OUT number --check
) AS
BEGIN
authen_check := 0;
If authen_send = authen_input Then
authen_check := 1;
dbms_output.put_line('success');
Else
authen_check := 0;
dbms_output.put_line('fail');
End If;
END AUTH_CHECK;
--
-- Usage
--
SET SERVEROUTPUT ON
Declare
aucode NUMBER(6);
inputcode NUMBER(6);
chk NUMBER(1);
Begin
chk := 0;
Select
SubStr(LTrim(LTrim(DBMS_RANDOM.value, '0.'),'0'),0,6) InTo aucode
From dual;
auth_check(aucode, &inputcode, chk);
end;

Anonymous PL/SQL Block to call a procedure with collection

I need to test a package procedure which accepts a PL/SQL table and returns SYS_REFCURSOR.
What I am looking for is Anonymous PL/SQL block which does the following :
populates PL/SQL table and pass to procedure
The relevant code is below:
create or replace type rec_typ as object
(
fname varchar2(10),
lname varchar2(10)
)
/
create or replace type rec_arr as table of rec_typ
/
create or replace package get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
);
end;
/
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
l_out rec_arr;
begin
open p_out for select * from table ( l_out );
end;
end;
/
You need to declare and populate a table collection, and declare a cursor variable; and then pass those into the procedure call; e.g.:
declare
l_rec rec_arr;
l_cur sys_refcursor;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, l_cur);
-- do something with the cursor
close l_cur;
end;
/
If you are just testing it and you're using SQL*Plus or SQL Developer you could simplify this to use a client bind variable:
variable cur refcursor;
declare
l_rec rec_arr;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, :cur);
end;
/
print cur;
or even more simply:
variable cur refcursor;
begin
get_emp_pkg.get_emp(rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry')), :cur);
end;
/
print cur;
and you could even replace that with an execute call, which is just a wrapper for an anonymous block anyway, so functionally almost identical.
Your procedure has a mistake though; you're declaring a local l_out variable and then opening the ref cursor based on that, but you don't ever populate it - so the ref cursor result set will always be empty. If you change it to use l_rec instead (though I would call that p_rec, with the p_ prefix indicating a parameters and leave the l_ prefix for local variables):
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
begin
open p_out for select * from table ( l_rec );
end get_emp;
end;
/
then the bind variable versions above both see:
PL/SQL procedure successfully completed.
FNAME LNAME
---------- ----------
Joe Bloggs
Mary Berry

Execute procedure with defined pl/sql table type in toad

I have a package as such:
CREATE OR REPLACE PACKAGE someschema.somepackage
AS
TYPE t_str_array IS TABLE OF VARCHAR2 (500)
INDEX BY BINARY_INTEGER;
PROCEDURE some_procedure_p (in_first IN NUMBER,
in_second IN VARCHAR2,
in_third IN t_str_array,
in_fourth IN date,
in_fifth IN date,
out_sixth_cur OUT t_some_ref);
END;
/
CREATE OR REPLACE PACKAGE BODY someschema.somepackage
AS
PROCEDURE some_procedure_p (in_first IN NUMBER,
in_second IN VARCHAR2,
in_third IN t_str_array,
in_fourth IN date,
in_fifth IN date,
out_sixth_cur OUT t_some_ref);
IS
BEGIN
FOR i IN in_third.FIRST .. in_third.LAST
LOOP
... do something
END LOOP COMMIT;
OPEN out_sixth_cur FOR
SELECT ... something;
END;
END somepackage;
How do I execute this procedure in toad? So far I've tried:
Right click on the procedure, click execute package, call code is generated automatically:
DECLARE
IN_FIRST NUMBER;
IN_SECOND VARCHAR2(32767);
IN_THIRD someschema.somepackage.t_str_array;
IN_FOURTH DATE;
IN_FIFTH DATE;
OUT_SIXTH_CUR someschema.somepackage.t_some_ref;
BEGIN
IN_FIRST := NULL;
IN_SECOND:= NULL;
IN_FOURTH := NULL;
IN_FIFTH := NULL;
OUT_SIXTH_CUR := NULL;
someschema.somepackage.some_procedure_p ( IN_FIRST, IN_SECOND, IN_THIRD, IN_FOURTH, IN_FIFTH, OUT_SIXTH_CUR );
:rc0_OUT_SIXTH_CUR := OUT_SIXTH_CUR;
COMMIT;
END;
I added:
IN_THIRD := t_str_array('something');
But when I ran it like this, I got the following error:
PLS-00201: identifier 'T_STR_ARRAY' must be declared
Why did I get this error if I already defined this type in the package spec?? I've tried many other ways as well but it always to complain about the type.
You need to fully qualify the type name when you do the assignment (at least to package level; the schema is redundant if it's your package anyway but doesn't hurt here), as well as when you declare it in your anonymous block:
IN_THIRD := someschema.somepackage.t_str_array('something');
There is nothing at database/schema level called t_str_array, and if you don't qualify it Oracle doesn't know it needs to come from the package. You might think it's obvious; but there's nothing stopping you having the same type name defined in more than one package, so you have to be clear and consistent.
But as you pointed out you then get
PLS-00222: no function with name 'T_STR_ARRAY' exists in this scope
... because it's a table type, not a varray, so it's instantiated when its declared. You don't need to explicitly instantiate it, which is why Toad hasn't done that for you. The documentation shows this type of collection is initialised as 'empty' rather than null.
To populate it you just assign a value, using an index position:
IN_THIRD(1) := 'something';
So the whole block would become:
DECLARE
IN_FIRST NUMBER;
IN_SECOND VARCHAR2(32767);
IN_THIRD someschema.somepackage.t_str_array;
IN_FOURTH DATE;
IN_FIFTH DATE;
OUT_SIXTH_CUR someschema.somepackage.t_some_ref;
BEGIN
IN_FIRST := NULL;
IN_SECOND:= NULL;
IN_THIRD(1) := 'something';
IN_FOURTH := NULL;
IN_FIFTH := NULL;
OUT_SIXTH_CUR := NULL;
someschema.somepackage.some_procedure_p ( IN_FIRST, IN_SECOND, IN_THIRD, IN_FOURTH, IN_FIFTH, OUT_SIXTH_CUR );
:rc0_OUT_SIXTH_CUR := OUT_SIXTH_CUR;
COMMIT;
END;
I'd suggest you consider changing the name of your type though; giving a table type a name that suggests it's an array is (clearly!) confusing. They are known as both associative arrays and index-by tables, so you could argue it's fine as it is, but I kind of assumed it was a varray based both on the name and how you were trying to use it. (I should have checked, of course).

trouble defining weakly defined ref cursor

I'm attempting to write a stored proc that takes in a number, n, and returns the first n results for a given query, exclusively locking those n rows. I'm a little new to SQL and I'm having a bit of difficulty matching data types correctly.
My package spec looks like this:
PACKAGE package IS
Type out_result_type is REF CURSOR;
PROCEDURE stored_proc
(in_n IN NUMBER DEFAULT 10,
out_list IN OUT out_result_type);
I then define the cursor in the procedure body, like so:
CURSOR OUT_RESULT_TYPE IS
SELECT a.id
FROM schema.table a
WHERE (some conditions) AND rownum <= in_n;
A bit later on I then try to extract the results of the cursor into the output variable:
OPEN OUT_RESULT_TYPE;
FETCH OUT_RESULT_TYPE INTO out_list; -- error on this line
CLOSE OUT_RESULT_TYPE;
But alas this code doesn't compile; oracle complains that out_list has already been defined with a conflicting data type. Any idea how I can resolve this issue? It's driving me crazy!
Thanks in advance.
CREATE OR REPLACE PACKAGE pkg_test
AS
TYPE tt_cur IS REF CURSOR;
PROCEDURE prc_cur (retval OUT tt_cur);
END;
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur (retval OUT tt_cur)
AS
BEGIN
OPEN retval
FOR
SELECT *
FROM dual;
END;
END;
If you want to lock, use:
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur (retval OUT tt_cur)
AS
BEGIN
OPEN retval
FOR
SELECT a.id
FROM schema.table a
WHERE (some conditions)
AND rownum <= in_n
ORDER BY
column
-- Never forget ORDER BY!
FOR UPDATE;
END;
END;
Two remarks:
A cursor doesn't lock.
You don't have to do Type out_result_type is REF CURSOR;, use default type sys_refcursor. See here: Oracle - How to have an out ref cursor parameter in a stored procedure?
Your out_list must be of wrong type. Consider (script run on 10.2.0.3):
CREATE TABLE t AS SELECT ROWNUM ID FROM all_objects WHERE ROWNUM <= 100;
CREATE OR REPLACE PACKAGE cursor_pck AS
TYPE out_result_type is REF CURSOR;
PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10,
p_out_list IN OUT out_result_type);
END cursor_pck;
/
If you want to select and lock the rows at the same time you would use the FOR UPDATE clause:
CREATE OR REPLACE PACKAGE BODY cursor_pck AS
PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10,
p_out_list IN OUT out_result_type) IS
BEGIN
OPEN p_out_list FOR SELECT a.id FROM t a WHERE ROWNUM <= p_in FOR UPDATE;
END stored_proc;
END cursor_pck;
/
With the following setup, you will call the procedure like this:
SQL> SET SERVEROUTPUT ON;
SQL> DECLARE
2 l_cursor cursor_pck.out_result_type;
3 l_id t.id%TYPE;
4 BEGIN
5 cursor_pck.stored_proc(3, l_cursor);
6 LOOP
7 FETCH l_cursor INTO l_id;
8 EXIT WHEN l_cursor%NOTFOUND;
9 dbms_output.put_line(l_id);
10 END LOOP;
11 END;
12 /
1
2
3
PL/SQL procedure successfully completed
This is not going to work the way it's written, because
out_list expects a cursor, not a cursor result.
The name out_result_type is already used for a type, so you can't redefine it to be a cursor in the same scope.
Oracle provides a pre-defined weak reference cursor: sys_refcursor. In usage it would look like:
CREATE OR REPLACE PACKAGE pkg_test
AS
PROCEDURE prc_cur (p_retval OUT sys_refcursor,
p_lookup IN VARCHAR2);
END pkg_test;
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur(p_retval OUT sys_refcursor
p_lookup IN VARCHAR2)
IS
BEGIN
OPEN retval FOR SELECT a.value
FROM tblname a
WHERE a.id <= p_lookup;
END prc_cur;
END pkg_test;
This saves you the trouble of needing to declare a type. The sys_refcursor is a pointer to a result set from an open cursor. If you are familiar with Java, it's the same concept as the java.sql.ResultSet object which provides a way to get at the results of a query.

plsql function must be declared

So I have this function called cal_approv that calculates a quantity of supply, by selecting
the sum of ordered quantity, the "present" quantity, and the min quantity of a giving product(idart)...fecthing information into cursor then inserting it into a result table(resultat), when i try to run this code
accept idarticle prompt 'Donner lid article : '
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number IS qte _app number;
qtot number;
qtestk number;
qtemin number;
begin
select sum(qte_com) into qtot from lig_com where idart=code_art;
select qtestk into qtestk from article where idart=code_art;
select qtemin into qtemin from article where idart=code_art;
if ((qtot/qtestk)>(2*qtemin)) then
qte_app := qtot-qtestk*1.5;
else
qte_app := qtot*1.2;
end if;
return qte_app;
end;
/
drop function cal_approv;
declare
cursor arts is select idart, desart,pu from article where qtestk <= qtemin;
ida number;
da number;
qa number;
pa number;
begin
open arts;
loop
fetch arts into ida, da, pa;
qa := cal_ap prov(&idarticle);
pa := pa*cal_approv(&idarticle);
exit when(arts%notfound);
insert into resultat values(ida,da, qa,pa);
end loop;
close arts;
end;
/
select * from resultat;
I get this error
PLS-00201: identifier 'CAL_APPROV' must be declared
any help is appreciated!
Here's what I get when I indent your first block of PL/SQL code:
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
qte_app number;
qtot number;
qtestk number;
qtemin number;
begin
select sum(qte_com) into qtot from lig_com where idart=code_art;
select qtestk into qtestk from article where idart=code_art;
select qtemin into qtemin from article where idart=code_art;
if ((qtot/qtestk)>(2*qtemin)) then
qte_app := qtot-qtestk*1.5;
else
qte_app := qtot*1.2;
end if;
return qte_app;
end;
/
Note that the end in the second-last line ends the function, not the anonymous PL/SQL block. This will cause an error because Oracle is expecting something like the following:
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
-- ...
begin
-- body of function omitted
end;
begin
-- do something with this function
end;
/
Also, to be clear, a function declared within a PL/SQL block, rather than with a CREATE OR REPLACE FUNCTION ... statement, exists only while that block is being executed. Once the block completes, the function no longer exists. So you can write the following block:
DECLARE
FUNCTION add_one(p_in IN NUMBER)
RETURN NUMBER IS
BEGIN
RETURN p_in + 1;
END;
BEGIN
dbms_output.put_line(TO_CHAR(add_one(4));
END;
/
This should write 5 to dbms_output, if that has been enabled.
On the other hand, this won't work:
DECLARE
FUNCTION add_one(p_in IN NUMBER)
RETURN NUMBER IS
BEGIN
RETURN p_in + 1;
END;
BEGIN
NULL;
END;
/
BEGIN
dbms_output.put_line(TO_CHAR(add_one(4));
END;
/
This doesn't work because the function add_one is no longer available when the first block completes. The second block will fail with an error because it cannot find the function add_one.
There is no point attempting to use DROP FUNCTION with your function. DROP FUNCTION can only drop stored functions, and because your function is declared within a PL/SQL block, it is not a stored function.
As I see it you have two alternatives:
declare the function as a stored function, using CREATE OR REPLACE FUNCTION ..., outside of any DECLARE block, or
move all of the code that uses the function into the same block that declares the function.
In the latter case, your code would look something like the following (abbreviated for clarity):
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
-- ...
begin
-- ...
end;
cursor arts is select idart, desart,pu from article where qtestk <= qtemin;
ida number;
da number;
qa number;
pa number;
begin
open arts;
loop
-- ...
end loop;
close arts;
end;
/