I'm learning about Collections and trying out Associative Arrays in Oracle 11g. I'm using SQL Developer to write and test my code below and I am getting the error which I can't troubleshoot :
Error Report
Missing IN OUT Parameter at index ::1
Code I have written is as follows:
---SIMPLE collections EXAMPLE
DECLARE
TYPE prospect_towns IS TABLE OF VARCHAR2 (25)
INDEX BY PLS_INTEGER;
a_big_towns prospect_towns; -- associative array
i PLS_INTEGER := 1; -- index for the array
v_counter NUMBER;
v_town VARCHAR2(25);
BEGIN
a_big_towns(1):='Birmingham';
a_big_towns(2):='London':
a_big_towns(3):='Manchester';
-- v_counter := 1;
FOR i IN 1..a_big_towns.COUNT
LOOP <<big towns>>
--v_town := a_big_towns(i);
DBMS_OUTPUT.PUT_LINE('Inside Loop, town is '||a_big_towns(i));
i= a_big_towns.next:
END LOOP<<big towns>>
END;
/
Any ideas what's wrong ?
The second of these lines:
a_big_towns(1):='Birmingham';
a_big_towns(2):='London':
a_big_towns(3):='Manchester';
... has a colon at the end, instead of a semicolon. That's causing the following a_big_towns to be interpreted as a bind variable name by the parser. So it should be:
a_big_towns(2):='London';
Once you get past that, this line isn't needed, and would need := instead of = if it was, and also has a colon instead of a semicolon at the end:
i= a_big_towns.next:
... so remove that completely.
I'm not sure the labels are really adding anything here, but if you do have a label it doesn't need to be repeated at the end, and the name can't have a space in it, so make it:
<<big_towns>>
FOR i IN 1..a_big_towns.COUNT LOOP
And this needs a semicolon at the dned:
END LOOP;
This SQL Fiddle compiles.
Related
Can I assign a value from cursor to index of array with attribute?
I have an oracle code like this :
cursor cursor1 is select name, value from table;
FOR loop1 IN cursor1 LOOP
array1.EXTEND;
array1(loop1).cur_name := cursor1.name;
array1(loop1).cur_value := cursor1.value;
END LOOP;
i tried to convert to postgresql like this, but it's getting error
CREATE FUNCTION function_name () RETURNS something AS $$
DECLARE
cursor1 cursor for select name, value from table;
array1 text[];
BEGIN
-- Do something
...
FOR loop1 IN cursor1 LOOP
array1[loop].cur_name := cursor1.name; --error here
array1[loop1].cur_value := cursor1.value; -- error here
END LOOP;
-- Do something
...
RETURN;
END;
is there any method to create an array with attibute name?
The Oracle function is returning a collection (An Associative Array if I remember correctly, but its been awhile). Postgres does NOT have collections, the closest data type is an array. However since your collection contains multiple columns, you need to create a UDT (user defined type}, then your function returns an array of that type. (Note I assumed the data types in the table. Correct as deeded.)
create type name_val as (name text, value integer);
create or replace function function_name ()
returns name_val[]
language plpgsql
as $$
declare
cursor1 cursor for
select name, value
from test
limit 10;
rec record;
array1 name_val[];
l_name_val name_val;
begin
-- do something
for rec in cursor1
loop
l_name_val.name = rec.name;
l_name_val.value = rec.value;
array1 = array1 || l_name_val;
end loop;
-- do something
return array1;
end;
$$;
There are a couple other option which avoid the cursor and looping altogether. Assuming you actually need any Array returned you can reduce the above function to a single sql statement:
create or replace function function_name3()
returns name_val[]
language sql
as $$
select array_agg((name, value)::name_val)
from test
limit 10;
$$;
Demo Here
UPDATE:
I noticed that subsequent to my answer you update the question from for loop1 in 1 .. 10 ... to for rec in cursor1 ... thus removing the resulting row limitation. You accomplish the same by just removing the Limit 10 clause.
I am getting Subscript beyond count error while executing the procedure. I am trying to populate an associative array in my procedure and then eventually use it to insert. I think there is somewhere error in initialization of the array
Type definitions in a package header
TYPE sonic_data_rec IS RECORD(
ep_a_num LOG_PICK.EP_A_NUM%TYPE,
wvy_num LOG_PICK.WVY_NUM%TYPE,
ltrl_name LOG_PICK.Ltrl_Name%TYPE,
vel_pick_sq_num LOG_PICK.w_vel_pick_sq_num%TYPE,
vel_sv_typ_cd LOG_PICK.W_VEL_SV_TYP_CD%TYPE,
vel_sv_dt LOG_PICK.W_VEL_SV_DT%TYPE,
w_vel_log_pick_cd LOG_PICK.w_vel_log_pick_cd%TYPE,
sq_num LOG_PICK.W_VEL_PICK_SQ_NUM%TYPE,
pick_cd LOG_PICK.W_VEL_LOG_PICK_CD%TYPE,
onew_tm LOG_PICK.W_VEL_ONE_WAY_TV_TM%TYPE,
sonic_log LOG_PICK.W_VEL_SOLOG_CALB_VSP%TYPE,
ms_dpth LOG_PICK.W_VEL_PICK_DPTH%TYPE,
tv_dpth LOG_PICK.W_VEL_PICK_DPTH%TYPE);
TYPE sonic_tab IS TABLE OF sonic_data_rec;
Procedure code, only relevant code of package_body posted
PROCEDURE LOAD_LOG_PICK
(p_wvy_num IN WELL_VEL_SURVEY.WVY_NUM%TYPE
,p_noofpts IN NUMBER
,p_data_list IN STRINGARRAY)
IS
l_son_array sonic_tab := sonic_tab();
BEGIN
count1 := 1;
LOOP
l_son_array(count1).ep_a_num := p_ep_a_num;
l_son_array(count1).wvy_num := p_wvy_num;
l_son_array(count1).vel_sv_typ_cd := 'L';
l_son_array(count1).vel_sv_dt := p_pick_date;
l_son_array(count1).vel_pick_sq_num := count1;
l_son_array(count1).w_vel_log_pick_cd := 'O';
l_son_array(count1).ms_dpth := l_ms_dpth;
l_son_array(count1).onew_tm := regexp_substr(p_data_list(count1), '[^ ]+', 1, 1);
l_son_array(count1).sonic_log := regexp_substr(p_data_list(count1), '[^ ]+', 1, 1);
EXIT WHEN count1=5000;
count1 := count1+1;
END LOOP;
You are attempting to populate a nested table, not an associative array.
If sonic_tab was an associative array, it would be declared as
TYPE sonic_tab IS TABLE OF sonic_data_rec INDEX BY BINARY_INTEGER;
See the Oracle documentation on PL/SQL collection types for further information about the different types of collections you can use in PL/SQL.
As you have a nested table, you need to add calls to sonic_tab.EXTEND to make the table larger. It starts with size 0 because when you create it by calling sonic_tab(), you aren't passing any records into the constructor. Either call sonic_tab.EXTEND(1); in each iteration of the loop before you try to add anything to that item of the table, or call sonic_tab.EXTEND(5000); once before the loop if you know in advance how many items you will have in the table.
EDIT: I'm having alot of trouble formatting this code for some reason please bear with me. Also i'm aware some code is missing. This is just one portion of the code.
I am simulating a batch load that is run nightly to do some load testing. The problem I face is that my auto-generated PK's exceed the columns datalength after 100 or so inserts. How would I cap off my Strings without violating the unique constraint while inserting around 20,000 rows per table. my goal is to get rid of the random strings due to a change in requirements.Below is the portion of code I'm having trouble with.
declare
l_cnt integer := 0;
t_cnt integer := 0;
c_cnt integer := 0;
f_cnt integer := 0;
i integer := 0;
TYPE T_EMPL_NO IS TABLE OF VARCHAR2(1000) INDEX BY BINARY_INTEGER;
TAB_EMPL_NO T_EMPL_NO;
TAB_SEC_PK T_EMPL_NO;
TAB_THR_PK T_EMPL_NO;
TAB_FTH_PK T_EMPL_NO;
begin
dbms_output.put_line('START LOAD TEST');
LOOP
i := i + 1;
TAB_EMPL_NO(l_cnt) := 'JB'||i;
TAB_SEC_PK(t_cnt) := dbms_random.string('L',6);
TAB_THR_PK(c_cnt) := dbms_random.string('L',1);
TAB_FTH_PK(f_cnt) := dbms_random.string('L',20);
Insert into AOMS.PARTS_MONTH_CLOSE(
NAMES OF COLUMNS HERE
) Values (
TAB_EMPL_NO(l_cnt),
TAB_SEC_PK(t_cnt),
TAB_THR_PK(c_cnt),
TAB_FTH_PK(f_cnt)
);
l_cnt := l_cnt + SQL%ROWCOUNT;
EXIT WHEN l_cnt = 100; -- change to record count 22k
END LOOP;
dbms_output.put_line('P2ACCTMO :Rows inserted: ' || l_cnt);
END;
/
As a bonus If I wanted the script to run for an hour but not exceed the amount of records that insert during a loop how would I do that? Thanks so much for any help.
To define a numeric datatype with a given range (let's say 1 to 1000, for the sake of argument) you can use a user-defined PL/SQL subtype. To do this you'd use something like
SUBTYPE MY_ZERO_TO_1K_SUBTYPE IS NUMBER(4,0) RANGE 0..1000;
and then define variables of this subtype just as you would any other variable:
nLimited_number MY_ZERO_TO1K_SUBTYPE;
You can set a variable of this type to 0, 1, 2, ..., 998, 999, 1000. However, if you set it to a negative value or a value greater than 1000 an ORA-06502: PL/SQL: numeric or value error exception will be raised.
I would suggest to go for SEQUENCES rather than using Random string
generation. In this way you wlll not face unique key violation and
also you can set the max seq limit to the datatype limit of the
column. Hope this information helps.
I'm working with stored procedures in netezza.
I want to loop over a range of values.
The upper bound on the loop is passed as a variable into the sproc by the user.
i.e. EXECUTE SPROC(12);
so problem is that Netezza (aginity workbench) won't accept this input variable as the upper bound on the loop.
i.e.
DECLARE
x alias as $1.
begin
for i in 1..x loop
...do stufff...
end loop;
end;
I know that this can be solved using loop and exit style loop but It's eating me up as to why i can't do the above given that the documentation suggests that it's possible to do so.
Anyone know why this doesn't work or how to make it work?
Thanks.
Clancy.
Please find below working example -
CREATE OR REPLACE PROCEDURE generateTime(integer)
LANGUAGE NZPLSQL RETURNS varchar(255) AS
BEGIN_PROC
DECLARE
p_abc integer;
p_bcd integer;
p_var1 ALIAS FOR $1;
BEGIN
p_bcd := ISNULL(p_var1, 10);
raise notice 'p_bcd=%',p_bcd;
FOR p_abc in 0..(p_bcd)
LOOP
raise notice 'Hello World %', p_abc;
END LOOP;
END;
END_PROC;
Hope this will help.
In this block of SAS data step code I am setting a Table from an SQL query called TEST_Table. This table contains multiple columns including a larger section of columns titled PREFIX_1 to PREFIX_20. Each column starts with PREFIX_ and then an incrementing number from 1 to 20.
What I would like to do is iteratively cycle through each column and analyze the value of that column.
Below is an example of what I am trying to go for. As you can see I would like to create a variable that increases on each iteration and then I use that count value as a part of the variable name I am checking.
data TEST_Data;
set TEST_Table;
retain changing_number;
changing_number=1;
do while(changing_number<=20);
if PREFIX_changing_number='BAD_IDENTIFIER' then do;
PREFIX_changing_number='This is a bad part';
end;
end;
run;
How would be the best way to do this in SAS? I know I can do it by simply checking each value individually from 1 to 20.
if PREFIX_1 = 'BAD_IDENTIFIER' then do;
PREFIX_1 = 'This is a bad part';
end;
if PREFIX_2 = ...
But that would be really obnoxious as later I will be doing the same thing with a set of over 40 columns.
Ideas?
SOLUTION
data TEST_Data;
set TEST_Table;
array SC $ SC1-SC20;
do i=1 to dim(SC);
if SC{i}='xxx' then do;
SC{i}="bad part";
end;
end;
run;
Thank you for suggesting Arrays :)
You need to look up Array processing in SAS. Simply put, you can do something like this:
data TEST_Data;
set TEST_Table;
*retain changing_number; Remove this - even in your code it does nothing useful;
array prefixes prefix:; *one of a number of ways to do this;
changing_number=1;
do while(changing_number<=20);
if prefixes[changing_number]='BAD_IDENTIFIER' then do;
prefixes[changing_number]='This is a bad part';
end;
end;
run;
A slightly better loop is:
do changing_number = 1 to dim(prefixes);
... loop ...
end;
As that's all in one step, and it is flexible with the number of array elements (dim = number of elements in the array).