PL-SQL delete value from variable - oracle

In a PL SQL procedure I've a variable(declare as NUMBER) as follows,
Store_Values
I'm loading some data into this variable by select query.So if this data matches to the value(some value which will be compared against variable in if statement),then how to delete matched value from variable.
below is the procedure:
CREATE OR REPLACE PROCEDURE UPDATE_SIG_NAME_MAPS(SignatureID NUMBER, RuleCateory VARCHAR2, SignatureMessage VARCHAR2, IsDelete NUMBER, IsNew NUMBER) AS
NumberOfValues NUMBER ;
BEGIN
select VALUE BULK COLLECT into NumberOfValues from list_details where LIST_CONFIG_ID in(fetching from other table's);
if NumberOfValues = SignatureID then
**delete from sometable with where clause**
end if;
insert variable remaining values to the table using for loop
END ;
/

As #Aleksej said, you are trying to fetch into a scalar variable using bulk collect so you need to declare your varible as below:
You can follow the instructions and modify your code accordingly.
CREATE OR REPLACE PROCEDURE UPDATE_SIG_NAME_MAPS (
SignatureID NUMBER,
RuleCateory VARCHAR2,
SignatureMessage VARCHAR2,
IsDelete NUMBER,
IsNew NUMBER)
AS
Type NumberOfValue is table of NUMBER;
NumberOfValues NumberOfValue;
BEGIN
SELECT VALUE
BULK COLLECT INTO NumberOfValues
FROM list_details
WHERE LIST_CONFIG_ID IN (fetching from other table's);
For rec in 1..NumberOfValues.count
loop
if NumberOfValues(rec) = SignatureID then
**delete from sometable with where clause**
insert variable remaining values to the table
end loop;
END ;

Related

Default Value for Collection in Oracle Stored Procedure

I have three tables: test_main (could be empty on start), test_sec (almost always has records) and binding table test_ref(storess unique id combinations of records from test_main and test_sec).
I need to create a package with procedures that deal with inserions and updates of records in test_main and test_ref. I need some help with it.
Here's the code of a package:
create or replace package TEST_PKG is
type t_num is table of number;
procedure ADD (arg_main_id number
, arg_name varchar2
, arg_sec_ids t_num);
end test_pkg;
create or replace package body test_pkg is
procedure ADD (arg_main_id number
, arg_name varchar2
, arg_sec_ids t_num)
is begin
insert into test_main (id, col_name)
values (arg_main_id, arg_name);
commit;
for i in arg_sec_ids.first .. arg_sec_ids.last loop
insert into test_ref (main_id, sec_id)
values (arg_main_id, arg_sec_ids(i));
end loop;
commit;
end ADD;
end test_pkg;
I plan to call this procedure in Oracle APEX 5.1 PL/SQL Dynamic Action as follows:
declare
selection apex_t_number
-- or "test_pkg.t_num" with values from "apex_string.split_numbers" added through a "for" loop later
begin
selection := apex_string.split_numbers(:P1_SELECT2,':');
-- for example, '1:2:3' string results in a [1,2,3] array
ADD (arg_main_id => :P1_MAIN_ID
, arg_name => :P1_MAIN_NAME
, arg_sec_ids => selection);
end;
But I also would like to be able to call it as ADD (arg_main_id => :P1_MAIN_ID, arg_name => :P1_MAIN_NAME) with a default value for arg_sec_ids.
So, is it possible to define default value for a collection in PL/SQL Procedure, and if so, how to?
You can define arg_sec_ids parameter as of in out style is of type t_num.
create or replace package test_pkg is
type t_num is table of number;
procedure add (
arg_main_id number,
arg_name varchar2,
arg_sec_ids in out t_num
);
end;
and suppose to initialize with arg_sec_ids := t_num(1,1,2,3,5,8); in the program body, and if you extend with an integer with value number of elements in the tuple, you can use the next values for arg_sec_ids such as arg_sec_ids(7),..,arg_sec_ids(10).
create or replace package body test_pkg is
procedure add (
arg_main_id number,
arg_name varchar2,
arg_sec_ids in out t_num
) is
begin
arg_sec_ids := t_num(1,1,2,3,5,8);
arg_sec_ids.extend(10);
insert into test_main (id, col_name)
values (arg_main_id, arg_name);
for i in arg_sec_ids.first .. arg_sec_ids.last loop
insert into test_ref (main_id, sec_id)
values (arg_main_id, arg_sec_ids(i));
end loop;
arg_sec_ids(7) := arg_sec_ids(5)+ arg_sec_ids(6);
dbms_output.put_line( ' arg_sec_ids(7)''s values is : '||arg_sec_ids(7) );
commit;
end add;
end;
If arg_sec_ids.extend(x) is omitted (where x>6 ), it's impossible to use arrays with index more than default length( which's 6 in this case ).

Error(4,5): PLS-00428: an INTO clause is expected in this SELECT statement

create or replace PROCEDURE find_Doctor (p_SSN in number) AS
BEGIN
select drName, drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
I've got this stored procedure and I just want to output the resulting table from that call. Is there an easy way to do this without declaring a temporary table? I can't just make is a normal SQL query because I have to call it from a java program.
In a Procedure you would need variable to hold the result output of the SQL query. You can then use the variable. Use this:
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER)
AS
var_nm VARCHAR2 (100);
var_ph NUMBER;
BEGIN
SELECT drName, drPhone
INTO var_nm, var_ph
FROM clients
WHERE SSN = p_SSN;
DBMS_OUTPUT.put_line ('Doc Name - ' || var_nm || 'Doc Ph. No-' || var_ph);
END find_Doctor;
Edit:
I can't just make is a normal SQL query because I have to call it from
a java program.
You can then use SYS_REFCUSOR to return results, which can be mapped to a JDBC ResultSet.
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER,
VAR OUT SYS_REFCURSOR)
AS
BEGIN
OPEN VAR FOR
SELECT drName, drPhone
FROM clients
WHERE SSN = p_SSN;
END find_Doctor;
You should define out parameters drName and drPhone:
create or replace PROCEDURE find_Doctor (p_SSN in number, p_drName OUT
VARCHAR2, p_drPhone OUT VARCHAR2) AS
BEGIN
select drName, drPhone
into p_drName, p_drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
Easy way is to learn tool you're used and best practices for it.
For me, best way is remove senseless procedure and just selects data you need. But if you adherent of 'SP only' approach you can use ref cursor to retrive required data:
create or replace function find_Doctor (p_SSN in number)
return sys_refcursor as
v_result sys_refcursor;
BEGIN
open v_result for
select drName, drPhone
from clients
where SSN = p_SSN;
return v_result;
END find_Doctor;

Table variable as in parameter to populate a table in oracle Stored procedure

Mostly I avoid table variables as input parameters for a stored procedure. Because I do not know how to handle them, but in this case I have no other option. I have a requirement where hundreds of records will be passed on to database from Oracle Agile PLM. What I have to do is to populate a table from the input records/list. For accomplishing this I have developed an object type and then a table type out of that object type.
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE AS OBJECT
(QUERY_REF VARCHAR2 (1000 BYTE),
COL_NAME VARCHAR2 (100 BYTE),
COL_LENGTH VARCHAR2 (50 BYTE),
COL_SEQ NUMBER)
/
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE_TABLE AS TABLE OF MD_TYPE
/
Stored Procedure:
CREATE OR REPLACE PROCEDURE SP_TEST2
(
P_MD_TABLE IN MD_TYPE_TABLE,
p_success OUT number
)
IS
BEGIN
INSERT INTO MDATA_TABLE
(
QUERY_REF ,
COL_NAME ,
COL_LENGTH ,
COL_SEQ
)
SELECT ea.*
FROM TABLE(P_MD_TABLE) ea;
p_success :=1;
EXCEPTION
WHEN OTHERS THEN
p_success := -1;
END SP_TEST2;
The problem is I do not know how to populate, first parameter P_MD_TABLE and then MDATA_TABLE. And the procedure compiles without any errors. I have not tested this procedure.
Any help please.
Procedure for loading MD_TYPE_TABLE by passing parameters to MD_TYPE
CREATE OR REPLACE PROCEDURE SP_UPLOAD_MD_TYPE
(
P_QUERY_REF VARCHAR2,
P_COL_NAME VARCHAR2,
P_COL_LENGTH VARCHAR2,
p_col_seq NUMBER,
p_no_of_rows_to_insert NUMBER,
p_num OUT NUMBER
)
IS
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
BEGIN
<<vartype>>
FOR i IN 1..p_no_of_rows_to_insert
LOOP
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE(P_QUERY_REF, P_COL_NAME, P_COL_LENGTH, p_col_seq);
END LOOP vartype;
SP_TEST2(p_type_tbl, p_num);
END;
You can populate a table type by using extend/ bulk collect
using extend
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
or using bulk collect
SELECT MD_TYPE(c1, c2... cn)
BULK COLLECT INTO p_type_tbl
FROM some_table;
Demo
DECLARE
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
p_num NUMBER;
BEGIN
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF2', 'COL_NAME2', 'COL_LENGTH2', 2);
SP_TEST2(p_type_tbl, p_num);
DBMS_OUTPUT.PUT_LINE(p_num);
END;
/
OutPut
1
SELECT * FROM MDATA_TABLE;
OutPut
QUERY_REF COL_NAME COL_LENGTH COL_SEQ
QUERY_REF1 COL_NAME1 COL_LENGTH1 1
QUERY_REF2 COL_NAME2 COL_LENGTH2 2

Oracle PL/SQL return array of rows

Dear Oracle Developers,
I have searched and googled to find a solution for my problem but nothing helped me.
Situation :
TABLE : CUSTOMER(....);
My problem is : I want to create a stored procedure say get_customers to return an array of customer rows. I have no idea how to get this working.
I tried to create a type customer_rec and using a cursor to retrieve no more than maxRows
create or replace procedure get_customers(maxRows IN NUMBER, ??? OUT ????)
How to define the OUT parameter ?
How to retrieve the rows in the array using a cursor ?
Thanks a lot
I would like to answer this in a way that discourages passing around arrays, when passing around cursors is a more sound approach. It doesn't exactly answer the question as posed, but it is an answer. Thinking cursors instead of thinking arrays is more efficient and thus more scalable. Also, it can be much easier code to maintain.
create table customer (
customer_id number(2) primary key,
customer_name varchar2(200) );
insert into customer values (1, 'Customer One');
insert into customer values (2, 'Customer Two');
insert into customer values (3, 'Customer Three');
insert into customer values (4, 'Customer Four');
insert into customer values (5, 'Customer Five');
insert into customer values (6, 'Customer Six');
insert into customer values (7, 'Customer Seven');
CREATE OR REPLACE PACKAGE cursor_not_array IS
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR;
END cursor_not_array;
CREATE OR REPLACE PACKAGE BODY cursor_not_array IS
c_max_customer_id CONSTANT NUMBER(2) := 99;
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR IS
v_result SYS_REFCURSOR;
BEGIN
OPEN v_result FOR
SELECT customer_id,
customer_name
FROM customer
WHERE customer_id BETWEEN p_id_start AND nvl(p_id_end, c_max_customer_id)
ORDER BY customer_id;
RETURN v_result;
END;
END cursor_not_array;
You could create a package like this:
create or replace package customers is
type customers_array is table of customer%rowtype index by binary_integer;
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array);
end customers;
create or replace package body customers is
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array) is
cursor c_customers is
select *
from customers;
where rownum <= maxRows;
i number := 1;
begin
for r in c_customers loop
customer_array(i) := r;
i := i + 1;
end loop;
end get_customers;
end customers;
And then call the get_customers procedure from wherever you want to...
first time create VARRAY type.
'create TYPE CUSTARRAY is VARRAY(100) OF VARCHAR2(30);'
varray limit is depends on you.
then create procedure that return CUSTARRAY type parameter.
`create
procedure prc_get_arr(p_maxrow in number, p_customers out custarray)
as
my_cust custarray := custarray();
cursor c_cust is select name from CUSTOMER where rownum<p_maxrow;
v_customer varchar2(64);
begin
open c_cust;
loop
fetch c_cust into v_customer;
exit when c_cust%notfound;
my_cust.extend;
my_cust(my_cust.count) := v_customer;
end loop;
close c_cust;
p_customers:=my_cust;
end;`
Now call this procedure
DECLARE
P_MAXROW NUMBER;
p_customers custarray;
v_cnt number:=0;
begin
P_MAXROW := 22;
prc_get_arr( p_maxrow => p_maxrow, p_customers => p_customers );
v_cnt:=p_customers.count;
for i in p_customers.first..p_customers.last loop
dbms_output.put_line('P_CUSTOMERS = ' || p_customers(i));
end loop;
end;
You can, using the SYS_REFCURSOR on your function output.
First you have to define a collection :
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
Then your procedure simply have to fetch the result into that collection.
You're procedure could be written as follow:
CREATE OR REPLACE PACKAGE your_pkg
AS
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array);
END your_pkg;
CREATE OR REPLACE PACKAGE BODY your_pkg
AS
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array)
IS
BEGIN
SELECT *
BULK COLLECT INTO pt_coustomers
FROM customers
WHERE rownum <= pn_max_rows;
END get_customers;
END your_pkg;

How can implement a procedure in ESQL (an internal procedure) from Oracle database

I create a store procedure in Oracle db that insert Customer to my Table
this is my code :
CREATE OR REPLACE PROCEDURE THP.INSERT_CUSTOMER(
P_CUSTNAME IN VARCHAR2,
P_CUSTLAST IN VARCHAR2,
P_CUSTFATHER IN VARCHAR2,
P_NATIONNO IN NUMBER,
P_BIRTHDAY IN VARCHAR2,
P_BIRHTPLACE IN VARCHAR2,
P_EMAIL IN VARCHAR2,
P_CUSTENAME IN VARCHAR2,
P_CUSTELAST IN VARCHAR2,
P_OWNID IN NUMBER,
P_CUSTTYPEID IN NUMBER,
P_GENDERID IN NUMBER,
P_BILLSTID IN NUMBER,
P_BILLSPID IN NUMBER,
P_IDNO IN varchar2,
RESULT OUT INTEGER) IS
CNT NUMBER;
BEGIN
RESULT := 1;
CNT := 0;
SELECT COUNT(1) INTO CNT FROM THP.TBCUSTOMER WHERE NATIONNO=P_NATIONNO ;
IF CNT=1 THEN
COMMIT;
RESULT := 1; --IF RECORD is EXIST
ELSE
BEGIN
INSERT INTO TBCUSTOMER(CUSTID,CUSTNAME,CUSTLAST,CUSTFATHER,NATIONNO,BIRTHDAY,BIRHTPLACE,EMAIL,CUSTENAME,CUSTELAST,OWNID,CUSTTYPEID,GENDERID,BILLSTID,BILLSPID,IDNO)
VALUES(CUSTID_SEQ.NEXTVAL,P_CUSTNAME,P_CUSTLAST,P_CUSTFATHER,P_NATIONNO,P_BIRTHDAY,P_BIRHTPLACE,P_EMAIL,P_CUSTENAME,P_CUSTELAST,P_OWNID,P_CUSTTYPEID,P_GENDERID,P_BILLSTID,P_BILLSPID,P_IDNO);
COMMIT;
RESULT :=0; --IF INSERT NEW COLUMN
END;
END IF;
END INSERT_CUSTOMER;
/
now I want use this procedure in ESQL and create it directly in ESQL not CALL it from Oracle database or other DB
would you please guide me a bout it...
General comments, not an answer ...
count(1)
count(1) = count(*), which is the standard form for "count the number of rows". count(1) has no advantages, so best to use count(*).
RESULT := 1
is redundant at the beginning of the procedure
CNT := 0
... also redundant. The variable name is not very meaningful, and might make people think of a rude word, so perhaps change it to rows_found.
Prefixing the arguments with P_ is not required. If you use one of them in a SQL statement and need to deconflict it from a database object name then prefix it with the procedure name, so you have:
WHERE NATIONNO= INSERT_CUSTOMER.NATIONNO
Is NATIONNO constrained to be unique in the customer table? If not, use:
SELECT COUNT(*)
INTO CNT
FROM THP.TBCUSTOMER
WHERE NATIONNO=INSERT_CUSTOMER.NATIONNO AND
ROWNUM = 1;
(12g will introduce the LIMIT SQL syntax, by the way).
Commiting in a procedure is often held to be bad practice, as the procedure often becomes part of a longer business transaction (eg. inserting a new customer and address) and the commit should be controlled by the application code.
Upper case code is harder to read than lower case -- that's why direction signs on motorways are not uppercase.
The begin-end block for the insert is not required at all.
"birhtplace" is spelled wrong.
So I'd suggest that what you want to convert to ESQL is actually:
create or replace procedure thp.insert_customer(
custname in varchar2,
custlast in varchar2,
custfather in varchar2,
nationno in number ,
birthday in varchar2,
birhtplace in varchar2,
email in varchar2,
custename in varchar2,
custelast in varchar2,
ownid in number ,
custtypeid in number ,
genderid in number ,
billstid in number ,
billspid in number ,
idno in varchar2,
result out integer) is
rows_found number;
begin
select count(*)
into rows_found
from thp.tbcustomer
where nationno=insert_customer.nationno;
if rows_found = 1 then
result := 1;
else
insert into
tbcustomer(
custid ,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno)
values(
custid_seq.nextval,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno);
result :=0;
end if;
end insert_customer;
/
To call an external procedure via ESQL, you need to first declare the external function.
Do it as below:
CREATE PROCEDURE DoSomething (IN in1 CHARACTER, OUT out1 CHARACTER, OUT out2 CHARACTER)
LANGUAGE DATABASE
EXTERNAL NAME "DoSomething";
Now you can call this function from your ESQL function as below:
CALL DoSomething(in,out1,out2) IN Database.yourschema.yourDB;
P.S. The parameters you are passing must be compatible with the parameters of your stored procedure.

Resources