Passsing two VARRAYS to a Procedure to pass them to a Index-By table - plsql

I try to pass two VARRAYs into a Procedure but when call it to test it I don't get any kind of respond from my database.
CREATE OR REPLACE PACKAGE PKG_TEST AS
TYPE PNO IS VARRAY(20) OF VARCHAR(20);
TYPE QTY IS VARRAY(20) OF INTEGER;
TYPE indexTest IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
PROCEDURE blatest(i_PNO IN PNO, i_QTY IN QTY);
END PKG_TEST;
/
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE blatest(i_PNO IN PNO , i_QTY IN QTY)
IS
V_COUNT_PNO INTEGER;
V_COUNT_QTY INTEGER;
bla_list indexTest;
name VARCHAR(20);
BEGIN
V_COUNT_PNO := i_PNO.COUNT;
V_COUNT_QTY := i_QTY.COUNT;
IF V_COUNT_PNO = V_COUNT_QTY THEN
FOR I IN 1..V_COUNT_PNO LOOP
bla_list(i_PNO(I)) := i_QTY(I);
END LOOP;
name := bla_list.FIRST;
WHILE name IS NOT null LOOP
dbms_output.put_line('Name: ' || name || ' is ' || TO_CHAR(bla_list(name)));
name := bla_list.NEXT(name);
END LOOP;
ELSE
dbms_output.put_line('Amount of Variables is not identical!');
END IF;
END blatest;
END PKG_TEST;
/
PKG_TEST.blatest(PKG_TEST.PNO('P123','P124'), PKG_TEST.QTY(2,3));
/
And if there is any easier way to fill in a Index-By table I dynamicly I am more than happy to read this ^^. Thanks in advance!

Call it as below, then you will get response:
set serveroutput on;
BEGIN
pkg_test.blatest (pkg_test.pno ('P123', 'P124'), pkg_test.qty (2, 3));
END;

Related

PL/SQL: I get expression 'I' cannot be used as an assignment target

My code:
create table info(str varchar2(30));
declare
cursor c(job emp_ast.job_id%type, dep emp_ast.department_id%type) is select employee_id
from emp_ast
where job_id=job and department_id=dep;
type t_job is table of emp_ast.job_id%type;
t t_job:=t_job();
emp emp_ast.employee_id%type;
i number(3);
begin
select job_id
bulk collect into t
from emp_ast;
for i in 10..270 loop
for j in 1..t.count loop
open c(i, t(j));
loop
fetch c into emp;
insert into info
values (i||' '||t(j)||' '||emp);
exit when c%notfound;
end loop;
i:=i+10;
end loop;
end loop;
end;
/
I get "expression 'I' cannot be used as an assignment target", reffering to the line where I increment i by 10. I am trying to save the department_id, employee_id and job_id as a string in a table for each department and each job.
At the point where you get that message, i refers to the loop control variable i defined in the line for i in 10..270 loop, not the int(3) variable defined earlier. In PL/SQL a loop definition defines a variable which is only accessible inside the loop, and which you cannot alter. I suggest you change the name of one or the other to make them unique.
EDIT
PL/SQL doesn't provide a way to step by more than 1 in a computed FOR loop. Instead, you will need to compute the desired department number value within the loop:
DECLARE
CURSOR c(job EMP_AST.JOB_ID%TYPE,
dep EMP_AST.DEPARTMENT_ID%TYPE)
IS SELECT EMPLOYEE_ID
FROM EMP_AST
WHERE JOB_ID = job AND
DEPARTMENT_ID = dep;
TYPE t_job IS TABLE OF EMP_AST.JOB_ID%TYPE;
t t_job := t_job();
emp EMP_AST.EMPLOYEE_ID%TYPE;
nDepartment NUMBER;
BEGIN
SELECT job_id
BULK COLLECT INTO t
FROM EMP_AST;
FOR i IN 1..27 LOOP
nDepartment := i * 10;
FOR j IN 1..t.COUNT LOOP
OPEN c(t(j), nDepartment);
LOOP
FETCH c INTO emp;
INSERT INTO info
VALUES (nDepartment || ' ' || t(j) || ' ' || emp);
EXIT WHEN c%notfound;
END LOOP; -- cursor c
CLOSE c;
END LOOP; -- j
END LOOP; -- i
END;
/
Note that in the code above the nDepartment value is computed within the i loop, which now increments from 1 to 27 instead of going from 10 to 270.

PL/SQL Remove Characters While Loop

Im working on an assignment, the goal being to write a procedure that has 2 inputs a string and a char and remove the char from the string.
Example: RemoveAll(123-456-789, '-') will output: 123456789.
create or replace procedure MyRemoveAll
(p_text varchar2, p_char_1 char) as
v_temp varchar2(100);
BEGIN
v_temp := p_text;
WHILE instr(v_temp, p_char_1) != 0 LOOP
v_temp := substr(v_temp, 1, instr(v_temp, p_char_1)-1)
|| substr(p_text, instr(v_temp, p_char_1)+1);
END LOOP;
dbms_output.put_line (v_temp);
END;
/
exec MyRemoveAll('123456789', '*');
exec MyRemoveAll('123-456789', '-');
exec MyRemoveAll('123*456*789', '*');
I don't get any errors when the procedure is created, and the first two executions work correctly. I've tried just running:
dbms_output.put_line (instr(123*456*789, *));
Which gave me an output of 4. So it should register for the condition of the while loop, but SQL Plus just stops at the loop. (Like it's doing an infinite loop, but it isn't).
Anyone have any ideas?
I think you need to replace p_text with v_temp in your second substr call. That way each time you loop through you are working with the updated v_temp and not the original p_text.
Here is what it would look like:
create or replace procedure MyRemoveAll
(p_text varchar2, p_char_1 char) as
v_temp varchar2(100);
BEGIN
v_temp := p_text;
WHILE instr(v_temp, p_char_1) != 0 LOOP
v_temp := substr(v_temp, 1, instr(v_temp, p_char_1)-1)
|| substr(v_temp, instr(v_temp, p_char_1)+1);
END LOOP;
dbms_output.put_line (v_temp);
END;
/
All three of your examples work with this code.
Bobby

Separating multiple rows of a select query using delimiter (Oracle)

I have a query
SELECT originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
The output in a linux script is as below:
09-OCT-17 04.59.33.758 AM -05:00 09-OCT-17 05.03.22.645 AM -05:00
there are two rows above each starting by date.
I would like to have the output like
09-OCT-17 04.59.33.758 AM -05:00;09-OCT-17 05.03.22.645 AM -05:00
This is just two rows, there can be many more, I would like it so that every row is separated via delimiter.
I have tried few options like
1) using listagg:
select listagg(originating_timestamp,', ') within group(order by originating_timestamp) csv
from sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
But this gives error:
ERROR at line 1:
ORA-01489: result of string concatenation is too long
2) using XMLAGG:
SELECT RTRIM(XMLAGG(XMLELEMENT(E,originating_timestamp,';').EXTRACT('//text()') ORDER BY originating_timestamp).GetClobVal(),',') AS LIST
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY')= to_char(systimestamp,'DD-MON-YY');
But the output is like :
2017-10-09T04:59:33.758-05:00;2017-10-09T05:03:22.645-05:00;
Which is also not correct.
e.g. Select username from dba_users. Suppose there are 10 users, i want those 10 usernames to be separated via delimiter.
Below two anonymous block example which may be helpful to solve your problem :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select 'a' ||',' into tmp from dual;
ELSE
select 'a' into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
Second :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(1000);
tmp VARCHAR2(1000);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') || ',' into tmp from dual;
ELSE
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
You should replace my query select with yours and of course the format of date you wanna get and replace comma with your desire character .
To make it working properly i wanna suggest you to create a cursor :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
cont NUMBER;
CURSOR C_1 IS
SELECT to_char(originating_timestamp,'your date format')
originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
BEGIN
cont := 0;
FOR C_row IN C_1 LOOP
IF cont != C_1%rowcount THEN
tmp := C_row.originating_timestamp ||',';
res := concat(res,tmp);
ELSE
tmp := C_row.originating_timestamp;
res := concat(res,tmp);
END IF;
cont := cont +1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/

ORA-21700 on SELECT FROM package-user-defined TABLE

I'm creating an Oracle package (MyPackage) where I have a public custom type table (ObjDataCollection) of custom type records (ObjData), which will be used as IN parameter for one of the Package functions (Calculate):
CREATE OR REPLACE PACKAGE MyPackage AS
TYPE ObjData IS RECORD (
t date NOT NULL := DATE '0001-01-01',
v number(9, 4)
);
TYPE ObjDataCollection IS TABLE
OF ObjData;
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number;
END MyPackage;
CREATE OR REPLACE PACKAGE BODY MyPackage AS
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number IS
res number(9, 4) := 0;
dateStart date;
dsv ObjData;
CURSOR q1 (dt date) IS
SELECT * FROM TABLE(DataSource) --Throws ORA-21700: Object does not exist or is marked for delete oracle.
WHERE t >= dt
ORDER BY t;
BEGIN
-- some irrelevant code
dateStart := DATE '2015-01-01';
OPEN q1(dateStart);
LOOP FETCH q1 INTO dsv;
EXIT WHEN q1%NOTFOUND;
res := res + dsv.v;
-- some irrelevant code
END LOOP;
CLOSE q1;
-- some irrelevant code
return res;
END Calculate;
END MyPackage;
I debbuged my code and I get the error on the second line of the cursor (marked in code):
ORA-21700: Object does not exist or is marked for delete oracle.
I'm using this data to execute my package:
CREATE TABLE TestTable (d date DEFAULT DATE '0001-01-01', v number(9, 4));
INSERT INTO TestTable VALUES (DATE '2015-01-01', 2.1);
INSERT INTO TestTable VALUES (DATE '2015-01-08', 3.1);
INSERT INTO TestTable VALUES (DATE '2015-01-15', 4.1);
INSERT INTO TestTable VALUES (DATE '2015-01-22', 5.1);
INSERT INTO TestTable VALUES (DATE '2015-01-29', 6.1);
INSERT INTO TestTable VALUES (DATE '2015-02-05', 7.1);
And this code to run a test:
CREATE OR REPLACE PROCEDURE TestMyPackage AS
res MyPackage.ObjDataCollection;
counter number(9, 4);
BEGIN
SELECT d, v
BULK COLLECT INTO res
FROM TestTable
ORDER BY v;
counter := MyPackage.Calculate(res);
END TestMyPackage;
Why I recieve this ORA-21700 exception?
PACKAGE BODY MyPackage AS
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number IS
res BINARY_FLoAT:= 0;
dateStart date;
dsv ObjData;
copy_of_DataSource ObjDataCollection;
procedure sortCollection(toSort in out ObjDataCollection)
is
type idx_coll is table of ObjData;
type sort_help is table of idx_coll index by varchar2(16 char);
v_sort sort_help;
v_temp varchar2(16);
v_result ObjDataCollection := new ObjDataCollection();
v_cnt PLS_INTEGER := 0;
begin
for i in nvl(toSort.first,1) .. nvl(toSort.last,-1) loop
v_temp := to_char(toSort(i).t,'yyyymmddhh24miss');
if v_sort.exists(v_temp) then
v_sort(v_temp).extend(1);
v_sort(v_temp)(v_sort(v_temp).count) := toSort(i);
else
v_sort(v_temp) := idx_coll(toSort(i));
end if;
end loop;
v_result.extend(toSort.count);
v_temp := v_sort.first;
while v_temp is not null loop
for i in nvl(v_sort(v_temp).first,1) .. nvl(v_sort(v_temp).last,-1) loop
v_cnt := v_cnt +1;
v_result(v_cnt) := v_sort(v_temp)(i);
end loop;
v_temp := v_sort.next(v_temp);
end loop;
toSort := v_result;
end;
BEGIN
-- some irrelevant code
copy_of_DataSource := DataSource;
dateStart := DATE '2015-01-01';
sortCollection(copy_of_DataSource);
for i in nvl(copy_of_DataSource.first,1) .. nvl(copy_of_DataSource.last,-1) loop
if copy_of_DataSource(i).t > dateStart then
res := res + copy_of_DataSource(i).v;
dbms_output.put_line(copy_of_DataSource(i).t);
end if;
end loop;
-- some irrelevant code
return res;
END Calculate;
END MyPackage;
As it was a task due to a date, I ended up fixing it by creating user-defined types and tables at schema level as suggested Arkadiusz Ɓukasiewicz in one of his comments. Thank you for your effort.
As is not what I wanted, I won't mark any answer as Correct.
Got the same problem and solved it by doing the following steps:
Drop package where type is defined
Drop package using that type
Compile package with type
Compile target package
I got this ORA-21700 after adding new constants in a package with types/constants. I guess there's some internal issue with dependencies but I haven't dug a lot.

Array Binding for Select Statement in dbms_sql (Oracle 11g)

My Problem is that I want to execute a dynamic SQL-Query within PL/SQL where I have a List of IDs as my Array Bind.
In the Oracle-Documentation I found some Examples how to join Lists of Numbers to an DML-Statement. (http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996963)
Now I am trying to make the same thing for Select-Statements.
I know that I can use Array-Binds for the execute immediate-Statement. But this has the disadvantage that I must know the exact number of Bind-Variables before executing the Statement. That is the reason why I have to use dbms_sql.
The following Example Returns only one Row, but it should return 3 rows. Does anyone know what the Problem with my Example is?
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
p_ids dbms_sql.number_table;
c number;
dummy NUMBER;
p_name varchar2(100);
begin
p_ids(1) := 2;
p_ids(2) := 3;
p_ids(3) := 4;
--
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, 'select name from PERSON where id in(:num_array)', DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_ARRAY(c, ':num_array', p_ids);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;
Here is my current solution for binding multiple values to a Select Statement, maybe someone can need it:
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
c number;
dummy NUMBER;
p_name varchar2(100);
xml$ varchar2(1000);
begin
--Generate a XML-List instead of dbms_sql.number_table:
xml$ := '<ids><id>2</id><id>3</id><id>4</id></ids>';
--
c := dbms_sql.open_cursor;
--Using XML-Functions for extracting the Values from the XML-String
DBMS_SQL.PARSE(c, 'select name
from PERSON
where id in(select extractvalue(value(x), ''id'')
from table(xmlsequence(xmltype(:ids).extract(''ids/*'')))x)'
, DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_variable(c, ':ids', xml$);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;

Resources