Considerations about associative arrays in PLSQL - sql

I'm learn PLSQL Collections and now I'm practicing associativa Arrays, first step, I've used PLSQL Methods for collections (FIRST, LAST, COUNT, PRIOR) and I've coded a simple LOOP for print on screen all the information within associative variable. It's a simple example about players of a random football team but I donĀ“t understand the steps to code a loop using collections. This Loop is working fine but I don't understand the procedure to create a associative Array Loop. I need considerations and "theory" about this PLSQL resources. Thanks.
DECLARE
TYPE spurs_players_type IS TABLE OF VARCHAR2(45)
INDEX BY PLS_INTEGER;
v_spurs_2006 spurs_players_type;
v_spurs_numero PLS_INTEGER;
BEGIN
v_spurs_2006(1) := 'Robinson';
v_spurs_2006(2) := 'Chimbonda';
v_spurs_2006(3) := 'Young';
v_spurs_2006(4) := 'Zokora';
v_spurs_2006(5) := 'Davids';
v_spurs_2006(6) := 'Taino';
v_spurs_2006(7) := 'Stalteri';
v_spurs_2006(8) := 'Jenas';
v_spurs_2006(10) := 'Keane';
v_spurs_2006(20) := 'Dawson';
v_spurs_2006(22) := 'Huddlestone';
v_spurs_2006(25) := 'Lennon';
v_spurs_2006(26) := 'King';
v_spurs_2006(35) := 'Dervite';
v_spurs_numero := v_spurs_2006.LAST;
WHILE v_spurs_numero IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE('Number of player ' ||
v_spurs_2006(v_spurs_numero) || ' is ' ||
v_spurs_numero);
v_spurs_numero := v_spurs_2006.PRIOR (v_spurs_numero);
END LOOP;
END;

.LAST and .PRIOR are collection methods, check the docs below:
Collection Methods

Related

Adressing multiple, similar variables/objects in Delphi

I am writing a program that uses many shapes and I need to create a procedure to turn them all white. The shapes in question are named SectorBorder1 to SectorBorder20.
Is there a way to adress the shapes like this or similarly?
SectorBorder[X].brush.color := ClWhite;
Inc(X);
...where X is the number (obviously), instead of having to do:
SectorBorder1.brush.color := ClWhite;
SectorBorder2.brush.color := ClWhite;
...
SectorBorder20.brush.color := ClWhite;
So basically being able to differentiate names through a variable. This is the only way I could think of describing it. (Sorry, could someone also maybe include a better discription?) Any advice would be greatly apreciated.
Use an array
private
SectorBorders: array[1..20] of TShape;
procedure TMyForm.FormCreate(Sender: TObject):
begin
SectorBorders[1] := SectorBorder1;
..
SectorBorders[20] := SectorBorder20;
end;
procedure TMyForm.SetAllToWhite;
var
X: Integer;
begin
for X := Low(SectorBorders) to High(SectorBorders) do
SectorBorders[X].Brush.Color := clWhite;
end;
As an alternative to an array you can use a List. If you use the TList from System.Generics.Containers you can easily pass through all elements of the list.
You use it like this:
interface
uses System.Generics.Collections; // you need this to get the TList, you also need your other refereces of course
...
protected
pShapes: TList<TShape>;
procedure TMyForm.FormCreate(Sender: TObject):
var
nLoop: Integer;
begin
Self.pShapes:=TList<TShape>.Create;
nLoop:=0;
while(nLoop<Self.ComponentCount) do
begin
if(Self.Components[nLoop] is TShape) then
pList.Add(TShape(Self.Components[nLoop]));
Inc(nLoop);
end;
end;
procedure TMyForm.SetAllToWhite;
var
pShape: TShape;
begin
for pShape in Self.pShapes do
pShape.Brush.Color := clWhite;
end;
Choosing whether you want to use an array or a TList will be partially down to preference, but also down to what else you may want to do with the collection of TShapes and how they are managed within the object.
You can use the FindComponent method of the form.
shape:= FindComponent('SectorBorder' + IntToStr(i)) as TShape;
if shape <> nil then
pShape.Brush.Color := clWhite;
See http://docwiki.embarcadero.com/CodeExamples/Sydney/en/FindComponent_(Delphi)

Transforming Strings

I need to accept the following strings and put them into a type collection and pass it to a procedure
String Cars = Dodge Charger||Ford Mustang||Chevy Camro||Ford GT
String Cost = 35,000||25,000||29,000|55,000
String CarDesc = Power House||Sweet Ride||Too Cool||Blow your door off
How do I transform the records so they will be like the following?
Cars:
Dodge Charger||35,000||Power House
Ford Mustang||25,00||Sweet Ride
Chevy Camro||29,000||Too Cool
Ford GT||55,000||Blow your door off
How do I parse them into an array?
The types:
create or replace TYPE "CAR_OBJ"
AS
OBJECT (CAR_NAME VARCHAR2 (50),
Price Number,
CarDesc VARCHAR2 (100));
/
create or replace TYPE "CAR_IN_ARR"
IS
TABLE OF CAR_OBJ;
/
procedure car_values (
p_in_upd_car car_in_arr,
p_out_upd_results out car_out_cur
)
as
I have tried all kinds of for loops and I just can't get it in the right order
Thank you soo much
The double delimiter || makes this hard, so I cheated by replacing them with ~. Probably there is a neater way to handle this with a single regex, or a more elaborate approach using substr and instr.
Also I've assumed the cost example should be 35,000||25,000||29,000||55,000.
The real code should probably confirm that all the strings contain the same number of delimiters. Also you might want to parse the cost value into a number.
declare
inCars long := 'Dodge Charger||Ford Mustang||Chevy Camro||Ford GT';
inCost long := '35,000||25,000||29,000||55,000';
inCarDesc long := 'Power House||Sweet Ride||Too Cool||Blow your door off';
type varchar2_tt is table of varchar2(50);
cars varchar2_tt := varchar2_tt();
costs varchar2_tt := varchar2_tt();
carDescs varchar2_tt := varchar2_tt();
begin
inCars := replace(inCars,'||','~');
inCost := replace(inCost,'||','~');
inCarDesc := replace(inCarDesc,'||','~');
cars.extend(regexp_count(inCars,'~') +1);
costs.extend(regexp_count(inCost,'~') +1);
carDescs.extend(regexp_count(inCarDesc,'~') +1);
for i in 1..cars.count loop
cars(i) := regexp_substr(inCars,'[^~]+', 1, i);
costs(i) := regexp_substr(inCost,'[^~]+', 1, i);
carDescs(i) := regexp_substr(inCarDesc,'[^~]+', 1, i);
dbms_output.put_line(cars(i) || '||' || costs(i) || '||' || carDescs(i));
end loop;
end;

Commit vs CommitRetaining

I have made a Delphi application, which inserts a row into Firebird database.
There was a problem with a query which a solved via CommitRetaining, but I read that it is not right thing to use, because it may affect the server being more slow. Strange thing happens when I use Commit only, query runs ok, but when I want to see if the row is inserted, Retainingit isn't. It only gets inserted on application terminate. But when using CommitRetaining, the row is inserted instantly.
What may cause the problem?
EDIT: Code using CommitRetaining
adqPom := TADQuery.Create(nil);
adqPom.Connection := form1.ADOConnection1;
adTransakcija := TADTransaction.Create(nil);
adTransakcija.Connection:=form1.ADOConnection1;
adqPom.Transaction:=adTransakcija;
adTransakcija.StartTransaction;
try
with adqPom do
begin
close;
sql.Clear;
sql.Add('insert into uplate(sifra,b_prijema,magacin,datum,iznos,b_uplate,b_izvoda,banka,godina,tr_rac,datum_dokument)');
sql.Add('values(:S,:BP,:M,:D,:I,:BU,:BI,:B,:G,:TR,:DD)');
ParamByName('S').Value := strtoint(edit1.Text);
if Form15.adoqDostavn.FieldValues['A1'] = 3 then
edit3.Text := '99999';
ParamByName('BP').Value := edit3.Text;
ParamByName('M').Value := edit2.Text;
ParamByName('D').Value := strtodate(edit4.Text);
ParamByName('I').Value := StrToFloat(edit5.Text);
ParamByName('BU').Value := Br_Uplate+1;
ParamByName('BI').Value := strtoint(Edit6.Text);
ParamByName('B').Value := Edit8.Text;
ParamByName('G').Value := 2006;
if Form15.adoqDostavn.FieldValues['A1'] = 3 then
ParamByName('TR').Value:= form15.adoqDostavn.FieldValues['B_PRIJEMA']
else
ParamByName('TR').Value:= Form15.adoqDostavn.FieldValues['B_DOST'];
ParamByName('DD').Value:=StrToDate(edit9.Text);
ExecSQL;
end;
adTransakcija.CommitRetaining;
except
adTransakcija.RollbackRetaining;
raise;
end;
FreeAndNil(adTransakcija);
FreeAndNil(adqPom);
EDIT: Code using Commit (actually property of a query is set to autocommit)
adqPom := TADQuery.Create(nil);
adqPom.Connection := form1.ADOConnection1;
with adqPom do
begin
close;
sql.Clear;
sql.Add('insert into uplate(sifra,b_prijema,magacin,datum,iznos,b_uplate,b_izvoda,banka,godina,tr_rac,datum_dokument)');
sql.Add('values(:S,:BP,:M,:D,:I,:BU,:BI,:B,:G,:TR,:DD)');
ParamByName('S').Value := strtoint(edit1.Text);
if Form15.adoqDostavn.FieldValues['A1'] = 3 then
edit3.Text := '99999';
ParamByName('BP').Value := edit3.Text;
ParamByName('M').Value := edit2.Text;
ParamByName('D').Value := strtodate(edit4.Text);
ParamByName('I').Value := StrToFloat(edit5.Text);
ParamByName('BU').Value := Br_Uplate+1;
ParamByName('BI').Value := strtoint(Edit6.Text);
ParamByName('B').Value := Edit8.Text;
ParamByName('G').Value := 2006;
if Form15.adoqDostavn.FieldValues['A1'] = 3 then
ParamByName('TR').Value:= form15.adoqDostavn.FieldValues['B_PRIJEMA']
else
ParamByName('TR').Value:= Form15.adoqDostavn.FieldValues['B_DOST'];
ParamByName('DD').Value:=StrToDate(edit9.Text);
ExecSQL;
end;
FreeAndNil(adqPom);
Commit free the transaction environment and CommitRetaining is a Commit that not free the transaction environment (cursors still open). You can use CommitRetaining in a process but at the end you must use Commit to release the memory.
Usually CommitRetainning is used to optimize a process (that include a big number of Begin/Commit), but at the end of you must this process use Commit to clear memory.

plsql reading text file in an array

When I run this portion of my code, which is inside a package, I get an error (specifically at the l_cnt := 1_cnt + 1 line for some reason and the code crashes. What could I be doing wrong? I am trying to read in a file of certs. Here's what I have so far:
v_certList arr_claims_t := arr_claims_t();
v_certLst VARCHAR2(2000);
f UTL_FILE.FILE_TYPE;
s VARCHAR2(200);
-- used for looping
l_cnt simple_integer := 0;
/*cop procedure*/
PROCEDURE COP_DATALOAD_V2 AS
arr_claims arr_claims_t;
arr_sql arr_sql_t;
BEGIN
f := UTL_FILE.FOPEN('V_COP',
'certs_file.txt',
'R',
2500);
-- populata our v_certlist of arr_claims_t
loop
utl_file.get_line(f, s);
v_certList.extend();
l_cnt := l_cnt+1;
v_certList(l_cnt) := s;
end loop;
exception
when no_data_found then
utl_file.fclose(f);
I want the array to be succesfuly populated given a text file (and I understand this is not the best practice but this is what I will have to do for now)
I figured out the error! The s that it was reading in was too big for the array. This was because empty spaces in the files was included.
v_certList.extend(1);
l_cnt := l_cnt + 1;
v_certList(l_cnt) := substr(s,
0,
10)
This fixed it for me.

Oracle pl/sql Change Array Size and Members After Delete

For example i have an array like
"a(1):=1 ,a(2):=2, a(3) := 3"
and now my array count =3 "(a.count)"
then i delete middle member "a.delete(2)" then i wanna make my array like this "a(1):=1;a(2):=3" and my array count = 2 ("a.count") how can i do this ?
ps:i need to this with big sized array so i think i should use, for or while loop but how...
The collection where you have deleted some element is called sparse collection. Below you have example how to iterate that type of collection and how to use it with forall.
declare
type a is table of number;
ar a;
v_idx number;
begin
select level bulk collect into ar from dual connect by level< 1000;
ar.delete(1);
ar.delete(4);
ar.delete(10);
ar.delete(88);
v_idx := ar.first;
while v_idx is not null loop
dbms_output.put_line('idx: '||v_idx ||' value:'|| ar(v_idx));
v_idx := ar.next(v_idx);
end loop;
-- FORALL i IN INDICES OF ar
-- INSERT INTO test_table VALUES ar(i);
end;
Thank you but i should change array too , i need to take same output when i print array members like
for i in ar.first..ar.last loop
dbms_output.put_line(ar(i));
end loop;
declare
type a is table of number;
ar a;
begin
select level bulk collect into ar from dual connect by level< 1000;
ar.delete(1);
ar.delete(4);
ar.delete(10);
ar.delete(88);
-- ar is sparse collection;
ar := ar MULTISET intersect ar;
-- ar is dense collection and for i in .... is possible
FOR i IN ar.first .. ar.last LOOP
DBMS_OUTPUT.put_line(ar(i));
END LOOP;
end;
you can try this approach assign values of first spared collection to second continues collection and use second collection for further processing...
declare
type num_arr is table of number;
v_num_arr1 num_arr; --first collection
v_num_arr2 num_arr := num_arr(); -- second collection initialization and declaration
v_idx number;
v_col_index number := 1;
begin
-- fill 10 element.
select level || '1' as num1 bulk collect into v_num_arr1 from dual connect by level < 10;
for x in v_num_arr1.first .. v_num_arr1.last loop
dbms_output.put_line('index: ' || x || ' value: ' || v_num_arr1(x));
end loop;
dbms_output.put_line('');
-- delete element
v_num_arr1.delete(3);
v_num_arr1.delete(7);
v_idx := v_num_arr1.first;
while v_idx is not null loop
dbms_output.put_line('index: ' || v_idx || ' value: ' || v_num_arr1(v_idx));
-- filling second collection with regular index by variable v_col_index
if v_num_arr1(v_idx) is not null then
v_num_arr2.extend(1);
v_num_arr2(v_col_index) := v_num_arr1(v_idx);
v_col_index := v_col_index + 1;
end if;
v_idx := v_num_arr1.next(v_idx);
end loop;
dbms_output.put_line('second collection elements
');
--check second colleciton
for x in v_num_arr2.first .. v_num_arr2.last loop
dbms_output.put_line('index: ' || x || ' value: ' || v_num_arr2(x));
end loop;
end;