Using .count in for loop - oracle

I'm new to oracle. I've come across table functions. The code for the function is
CREATE TYPE t_tf_row AS OBJECT (
id NUMBER,
description VARCHAR2(50));
CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
CREATE OR REPLACE FUNCTION get_tab_tf (p_rows IN NUMBER) RETURN t_tf_tab AS
l_tab t_tf_tab := t_tf_tab();
BEGIN
FOR i IN 1 .. p_rows LOOP
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for ' || i);
END LOOP;
RETURN l_tab;
END;
Now, I want to create a procedure which will update the table type using for loop.
The code for the procedure is
create or replace procedure add_n_rows(n_rows in number) is
l_tab t_tf_tab := t_tf_tab();
begin
for i in t_tf_tab.count .. t_tf_tab.count + n_rows
loop
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
I'm getting error saying that COUNT should be declared.
ORA-06550: line 1, column 37:
PLS-00302: component 'COUNT' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
Could you help me out in using the right procedure for .count in for loop.Thanks

You need to use the variable name not the type.
create or replace procedure add_n_rows(
n_rows in number)
is
l_tab t_tf_tab := t_tf_tab();
begin
for i in l_tab.count .. 1_tab.count + n_rows
loop
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
/

Related

Oracle PLSQL wrong number or types of arguments in call to 'BIND_ARRAY' in DBMS_SQL

I've written the below PLSQL block.
However when i execute it , i am getting following error.
Kindly tell me how to pass nested tables in the DBMS_SQL to
execute and generate the report.
Can we use DBMS_SQL to pass array value to bind
parameters ?
Or else do we have to use Execute Immediate only.
We are using DBMS_SQL for generating all the reports.
Error report -
ORA-06550: line 34, column 2:
PLS-00306: wrong number or types of arguments in call to 'BIND_ARRAY'
ORA-06550: line 34, column 2:
PL/SQL: Statement ignored
ORA-06550: line 35, column 2:
PLS-00306: wrong number or types of arguments in call to 'BIND_ARRAY'
ORA-06550: line 35, column 2:
declare
l_cursor number := dbms_sql.open_cursor;
l_ignore number;
q varchar2(32000);
o_desc_tab DBMS_SQL.desc_tab;
l_col_cnt number(10);
l_val VARCHAR2 (32767);
l_rec_no NUMBER;
l_col_sep VARCHAR2(25) := ',';
l_text VARCHAR2 (32767);
l_value VARCHAR2 (4000);
TYPE T_dc_code IS TABLE OF VARCHAR2(25);
A_dc_code T_dc_code;
TYPE T_bin_type IS TABLE OF VARCHAR2(25);
A_bin_type T_bin_type;
begin
q :='select col1, col2, col3, col4
from tabl1 wbm
inner join table2 wam
on wbm.dc_code=wam.dc_code
and wbm.dc_area=wam.dc_area
where wbm.dc_code MEMBER OF :P_DC_CODE
and wbm.BIN_TYPE MEMBER OF :P_BIN_TYPE
)';
dbms_sql.parse( l_cursor,q,dbms_sql.native );
A_dc_code.EXTEND(2);
A_dc_code(1) := '888';
A_dc_code(2) := '902';
A_bin_type.EXTEND(2);
A_bin_type(1) := 'R';
A_bin_type(2) := 'P';
dbms_sql.bind_array( l_cursor, ':P_DC_CODE', A_bin_type );
dbms_sql.bind_array( l_cursor, ':P_BIN_TYPE', A_dc_code );
DBMS_SQL.describe_columns (l_cursor, l_col_cnt, o_desc_tab);
FOR l_cl_cnt IN 1 .. o_desc_tab.COUNT
LOOP
DBMS_SQL.define_column (l_cursor, l_cl_cnt, l_val, 32767);
END LOOP;
l_ignore := dbms_sql.execute( l_cursor );
WHILE (DBMS_SQL.fetch_rows (l_cursor) > 0)
LOOP
DBMS_OUTPUT.PUT_LINE('COMING IN LOOP');
FOR l_rec_no IN 1 .. o_desc_tab.COUNT
LOOP
DBMS_SQL.COLUMN_VALUE (l_cursor, l_rec_no, l_value);
l_text := l_text || l_col_sep || l_value;
END LOOP;
END LOOP;
dbms_sql.close_cursor( l_cursor );
DBMS_OUTPUT.PUT_LINE('THE VALUE IS ---- '||l_text);
end;
/
Thanks
You have to use one of the pre-defined types, e.g. DBMS_SQL.varchar2_table, see Types for Scalar and LOB Collections.
Using your own TYPE T_bin_type IS TABLE OF VARCHAR2(25) is not possible
A_bin_type DBMS_SQL.varchar2_table;
BEGIN
A_bin_type(1) := 'R';
A_bin_type(2) := 'P';

Dynamic select execution missing expression error

I am using Oracle 12, and I want to make a dynamic procedure which selects rows from specific table but according to an unknown conditio. That condition will be specified as input parameter.
Suppose I have a column called employee id and I want to call the procedure
with the following condition
execute s('employeeid = 2')
My code is
create or replace procedure s (condition varchar)
as
TYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR type
emp_cv EmpCurTyp; -- declare cursor variable
my_ename VARCHAR2(15);
my_sal NUMBER := 2;
mycondition varchar2(100):=condition;
BEGIN
OPEN emp_cv FOR -- open cursor variable
'SELECT employeeid, employeename FROM employees WHERE = :s' USING mycondition;
END;
but I am getting an error
missing expression
What am I doing wrong, and will the result of this procedure be selected rows from employees table that satisfy applied condition ?
The USING is meant to handle values, not pieces of code; if you need to edit your query depending on an input parameter ( and I believe this is a very dangerous way of coding), you should treat the condition as a string to concatenate to the query.
For example, say you have this table:
create table someTable(column1 number)
This procedure does somthing similar to what you need:
create or replace procedure testDyn( condition IN varchar2) is
cur sys_refcursor;
begin
open cur for 'select column1 from sometable where ' || condition;
/* your code */
end;
Hot it works:
SQL> exec testDyn('column1 is null');
PL/SQL procedure successfully completed.
SQL> exec testDyn('column99 is null');
BEGIN testDyn('column99 is null'); END;
*
ERROR at line 1:
ORA-00904: "COLUMN99": invalid identifier
ORA-06512: at "ALEK.TESTDYN", line 4
ORA-06512: at line 1
This is not embedded in a procedure yet but I tested this and works:
DECLARE
TYPE OUT_TYPE IS TABLE OF VARCHAR2 (20)
INDEX BY BINARY_INTEGER;
l_cursor INTEGER;
l_fetched_rows INTEGER;
l_sql_string VARCHAR2 (250);
l_where_clause VARCHAR2 (100);
l_employeeid VARCHAR2 (20);
l_employeename VARCHAR2 (20);
l_result INTEGER;
o_employeeid OUT_TYPE;
o_employeename OUT_TYPE;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
l_sql_string := 'SELECT employeeid, employeename FROM employees WHERE ';
l_where_clause := 'employeeid = 2';
l_sql_string := l_sql_string || l_where_clause;
DBMS_SQL.PARSE (l_cursor, l_sql_string, DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
1,
l_employeeid,
20);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
2,
l_employeename,
20);
l_fetched_rows := 0;
l_result := DBMS_SQL.EXECUTE_AND_FETCH (l_cursor);
LOOP
EXIT WHEN l_result = 0;
DBMS_SQL.COLUMN_VALUE (l_cursor, 1, l_employeeid);
DBMS_SQL.COLUMN_VALUE (l_cursor, 2, l_employeename);
l_fetched_rows := l_fetched_rows + 1;
o_employeeid (l_fetched_rows) := l_employeeid;
o_employeename (l_fetched_rows) := l_employeename;
l_result := DBMS_SQL.FETCH_ROWS (l_cursor);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (l_cursor);
DBMS_OUTPUT.PUT_LINE (o_employeeid (1));
DBMS_OUTPUT.PUT_LINE (o_employeename (1));
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('GENERAL FAILURE: ' || SQLERRM);
END;

Oracle PL/SQL Ref Cursor Function Missing Char

When I run this code, the result should be 636790 but 63679 is only returned. Beating brains out on this one!!! Why the missing digit? Source table and column contain correct number of 636790.
create or replace package jt_types
as
type ttest is ref cursor;
end;
CREATE OR REPLACE FUNCTION jt_test
RETURN jt_types.ttest
IS
o_return jt_types.ttest;
BEGIN
OPEN o_return FOR
SELECT offn_id
FROM jt_offn_4
where offn_ID in (636790)
ORDER BY offn_id;
RETURN o_return;
END jt_test;
DECLARE
l_ids jt_types.ttest;
l_id NUMBER;
BEGIN
l_ids := jt_test;
LOOP
FETCH l_ids INTO l_id;
EXIT WHEN l_ids%NOTFOUND;
dbms_output.put_line('Rec: ' || l_id);
END LOOP;
CLOSE l_ids;
END;
The easiest way to do is to return sys_refcursor, here is the code:
create or replace
function jt_test
return sys_refcursor
is
o_return sys_refcursor;
begin
open o_return for
select offn_id
from jt_offn_4
where offn_ID = 636790;
return o_return;
end jt_test;
declare
l_ids sys_refcursor;
l_id number;
begin
l_ids := jt_test;
loop
fetch l_ids into l_id;
dbms_output.put_line('Rec: ' || l_id);
exit when l_ids%NOTFOUND;
end loop;
close l_ids;
end;

Procedure to update a table which is already populated

I want to create a procedure that adds new rows to an already existing table.But with the current procedure I've, I'm rewriting the entire table. The code for the current procedure is
CREATE TYPE t_tf_row AS OBJECT (
id NUMBER,
description VARCHAR2(50));
CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
create or replace procedure add_n_rows(
n_rows in number)
is
l_tab t_tf_tab := t_tf_tab();
begin
for i in l_tab.count .. l_tab.count + n_rows
loop
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
Here, everytime I'm rewriting the entire l_tab. I want to update the one which is already updated. Suggest me the right method for the required procedure.Thanks
This is because you're re-creating the object. You need to pass an instantiated version of the object into the procedure as a parameter:
create or replace procedure add_n_rows(
Pn_rows in number
, P_tab in out t_tf_tab ) is
begin
for i in P_tab.count .. P_tab.count + Pn_rows
loop
P_tab.extend;
P_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
I've declared P_tab as an OUT parameter, this means you can alter it. If you don't want to do this then remove "out" and declare a local variable of the type t_tf_tab, which you can then alter.
You can then call it separately, for instance:
declare
l_tab t_tf_tab := t_tf_tab();
begin
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(1. 'Hello');
add_n_rows(3, l_tab);
end;

Oracle PL/SQL - How to create a simple array variable?

I'd like to create an in-memory array variable that can be used in my PL/SQL code. I can't find any collections in Oracle PL/SQL that uses pure memory, they all seem to be associated with tables. I'm looking to do something like this in my PL/SQL (C# syntax):
string[] arrayvalues = new string[3] {"Matt", "Joanne", "Robert"};
Edit:
Oracle: 9i
You can use VARRAY for a fixed-size array:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t('Matt', 'Joanne', 'Robert');
begin
for i in 1..array.count loop
dbms_output.put_line(array(i));
end loop;
end;
Or TABLE for an unbounded array:
...
type array_t is table of varchar2(10);
...
The word "table" here has nothing to do with database tables, confusingly. Both methods create in-memory arrays.
With either of these you need to both initialise and extend the collection before adding elements:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t(); -- Initialise it
begin
for i in 1..3 loop
array.extend(); -- Extend it
array(i) := 'x';
end loop;
end;
The first index is 1 not 0.
You could just declare a DBMS_SQL.VARCHAR2_TABLE to hold an in-memory variable length array indexed by a BINARY_INTEGER:
DECLARE
name_array dbms_sql.varchar2_table;
BEGIN
name_array(1) := 'Tim';
name_array(2) := 'Daisy';
name_array(3) := 'Mike';
name_array(4) := 'Marsha';
--
FOR i IN name_array.FIRST .. name_array.LAST
LOOP
-- Do something
END LOOP;
END;
You could use an associative array (used to be called PL/SQL tables) as they are an in-memory array.
DECLARE
TYPE employee_arraytype IS TABLE OF employee%ROWTYPE
INDEX BY PLS_INTEGER;
employee_array employee_arraytype;
BEGIN
SELECT *
BULK COLLECT INTO employee_array
FROM employee
WHERE department = 10;
--
FOR i IN employee_array.FIRST .. employee_array.LAST
LOOP
-- Do something
END LOOP;
END;
The associative array can hold any make up of record types.
Hope it helps,
Ollie.
You can also use an oracle defined collection
DECLARE
arrayvalues sys.odcivarchar2list;
BEGIN
arrayvalues := sys.odcivarchar2list('Matt','Joanne','Robert');
FOR x IN ( SELECT m.column_value m_value
FROM table(arrayvalues) m )
LOOP
dbms_output.put_line (x.m_value||' is a good pal');
END LOOP;
END;
I would use in-memory array. But with the .COUNT improvement suggested by uziberia:
DECLARE
TYPE t_people IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER;
arrayvalues t_people;
BEGIN
SELECT *
BULK COLLECT INTO arrayvalues
FROM (select 'Matt' m_value from dual union all
select 'Joanne' from dual union all
select 'Robert' from dual
)
;
--
FOR i IN 1 .. arrayvalues.COUNT
LOOP
dbms_output.put_line(arrayvalues(i)||' is my friend');
END LOOP;
END;
Another solution would be to use a Hashmap like #Jchomel did here.
NB:
With Oracle 12c you can even query arrays directly now!
Another solution is to use an Oracle Collection as a Hashmap:
declare
-- create a type for your "Array" - it can be of any kind, record might be useful
type hash_map is table of varchar2(1000) index by varchar2(30);
my_hmap hash_map ;
-- i will be your iterator: it must be of the index's type
i varchar2(30);
begin
my_hmap('a') := 'apple';
my_hmap('b') := 'box';
my_hmap('c') := 'crow';
-- then how you use it:
dbms_output.put_line (my_hmap('c')) ;
-- or to loop on every element - it's a "collection"
i := my_hmap.FIRST;
while (i is not null) loop
dbms_output.put_line(my_hmap(i));
i := my_hmap.NEXT(i);
end loop;
end;
Sample programs as follows and provided on link also https://oracle-concepts-learning.blogspot.com/
plsql table or associated array.
DECLARE
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary;
name VARCHAR2(20);
BEGIN
-- adding elements to the table
salary_list('Rajnish') := 62000; salary_list('Minakshi') := 75000;
salary_list('Martin') := 100000; salary_list('James') := 78000;
-- printing the table name := salary_list.FIRST; WHILE name IS NOT null
LOOP
dbms_output.put_line ('Salary of ' || name || ' is ' ||
TO_CHAR(salary_list(name)));
name := salary_list.NEXT(name);
END LOOP;
END;
/
Using varray is about the quickest way to duplicate the C# code that I have found without using a table.
Declare your public array type to be use in script
type t_array is varray(10) of varchar2(60);
This is the function you need to call - simply finds the values in the string passed in using a comma delimiter
function ConvertToArray(p_list IN VARCHAR2)
RETURN t_array
AS
myEmailArray t_array := t_array(); --init empty array
l_string varchar2(1000) := p_list || ','; - (list coming into function adding final comma)
l_comma_idx integer;
l_index integer := 1;
l_arr_idx integer := 1;
l_email varchar2(60);
BEGIN
LOOP
l_comma_idx := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_idx = 0;
l_email:= SUBSTR(l_string, l_index, l_comma_idx - l_index);
dbms_output.put_line(l_arr_idx || ' - ' || l_email);
myEmailArray.extend;
myEmailArray(l_arr_idx) := l_email;
l_index := l_comma_idx + 1;
l_arr_idx := l_arr_idx + 1;
END LOOP;
for i in 1..myEmailArray.count loop
dbms_output.put_line(myEmailArray(i));
end loop;
dbms_output.put_line('return count ' || myEmailArray.count);
RETURN myEmailArray;
--exception
--when others then
--do something
end ConvertToArray;
Finally Declare a local variable, call the function and loop through what is returned
l_array t_array;
l_Array := ConvertToArray('email1#gmail.com,email2#gmail.com,email3#gmail.com');
for idx in 1 .. l_array.count
loop
l_EmailTo := Trim(replace(l_arrayXX(idx),'"',''));
if nvl(l_EmailTo,'#') = '#' then
dbms_output.put_line('Empty: l_EmailTo:' || to_char(idx) || l_EmailTo);
else
dbms_output.put_line
( 'Email ' || to_char(idx) ||
' of array contains: ' ||
l_EmailTo
);
end if;
end loop;

Resources