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.
Related
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
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.
Just to make it clear, I do not want to copy the entire TSQLQuery into the TdxMemData, as I would use memds.CopyFromDataSet(qry) for that.
I am interating through each record from the TSQLQuery, and I may or may not be adding a record(s) to the TdxMemData. Generally the record in memds matches that in qry, but sometimes the values are altered and sometimes additional records are added to memds. My example did not make this clear since all it seemed to do was copy over each record.
So given an active record in the TSQLQuery, I want to copy over the values into an active editable record in the TdxMemData.
The following code works in so far as it creates a copy of the record:
qry := TSQLQuery.Create(nil);
memds := TdxMemData.Create(nil);
try
qry.SQLConnection := cn;
qry.Text := 'SELECT Field1, Field2, Field3 FROM Table1';
qry.Open
memds.CreateFieldsFromDataSet(qry);
memds.Open;
while not qry.Eof do
begin
if {some condition} then
begin
memds.Append;
for i := 0 to qry.FieldCount-1 do
memds.Fields[i+1].Value := qry.Fields[i].Value; //First field is RecID
//Do something with the current memds record
end
else if {some other condition} then
begin
memds.Append;
//change values
memds.Append;
//change values
memds.Append;
//change values
end
else if {a third condition} then
; //Skip any work on memds
qry.next;
end;
qry.Close;
//Do something with memds
memds.Close;
finally
memds.Free;
qry.Free;
end;
Is there a better way? I had looked at AppendRecord but creating the array of TVarRec doesn't seem to be straightforward.
EDIT:
Let's use these examples with very simplified criteria. Note that the actual conditions that determine how many records to append and the changes to the field values in the destination are complex and not in any database.
Method 1:
While not tblSource.Eof do
Begin
If (iCondition = 1) Then
Begin
// Add one record
tblDestination.Append;
tblDestination.FieldByName('Field1').Value := tblSource.FieldByName('Field1').Value;
tblDestination.FieldByName('Field2').Value := tblSource.FieldByName('Field2').Value;
tblDestination.FieldByName('Field3').Value := tblSource.FieldByName('Field3').Value;
tblDestination.FieldByName('Field4').Value := tblSource.FieldByName('Field4').Value;
tblDestination.FieldByName('Field5').Value := tblSource.FieldByName('Field5').Value;
if bSomethingCondition then
tblDestination.FieldByName('Field4').Value := 'Something';
End
Else If (iCondition = 2) Then
Begin
// Add two records
tblDestination.Append;
tblDestination.FieldByName('Field1').Value := tblSource.FieldByName('Field1').Value;
tblDestination.FieldByName('Field2').Value := tblSource.FieldByName('Field2').Value;
tblDestination.FieldByName('Field3').Value := tblSource.FieldByName('Field3').Value;
tblDestination.FieldByName('Field4').Value := tblSource.FieldByName('Field4').Value;
tblDestination.FieldByName('Field5').Value := tblSource.FieldByName('Field5').Value;
if bAnotherThingCondition then
tblDestination.FieldByName('Field4').Value := 'Another thing';
tblDestination.Append;
tblDestination.FieldByName('Field1').Value := tblSource.FieldByName('Field1').Value;
tblDestination.FieldByName('Field2').Value := tblSource.FieldByName('Field2').Value;
tblDestination.FieldByName('Field3').Value := tblSource.FieldByName('Field3').Value;
tblDestination.FieldByName('Field4').Value := tblSource.FieldByName('Field4').Value;
tblDestination.FieldByName('Field5').Value := tblSource.FieldByName('Field5').Value;
if bSomethingElseCondition then
tblDestination.FieldByName('Field4').Value := 'Something else';
End
Else If (iCondition = 0) Then
Begin
// Add no records
End;
tblSource.Next;
End;
Since the number of fields in the source and destination tables can vary, hard-coding field names as in Method 1 is not suitable.
Method 2:
While not tblSource.Eof do
Begin
If (iCondition = 1) Then
Begin
// Add one record
tblDestination.Append;
for i := 0 to tblSource.FieldCount-1 do
tblDestination.Fields[i+1].Value := tblSource.Fields[i].Value;
if bSomethingCondition then
tblDestination.Fields(iSomethingConditionFieldIndex).Value := 'Something';
End
Else If (iCondition = 2) Then
Begin
// Add two records
tblDestination.Append;
for i := 0 to tblSource.FieldCount-1 do
tblDestination.Fields[i+1].Value := tblSource.Fields[i].Value;
if bAnotherThingCondition then
tblDestination.Fields(iAnotherThingConditionFieldINdex).Value := 'Another thing';
tblDestination.Append;
for i := 0 to tblSource.FieldCount-1 do
tblDestination.Fields[i+1].Value := tblSource.Fields[i].Value;
if bSomethingElseCondition then
tblDestination.Fields(iSomethingElseConditionFieldIndex).Value := 'Something else';
End
Else If (iCondition = 0) Then
Begin
// Add no records
End;
tblSource.Next;
End;
While method 2 above does work, and is the way it is currently done, this question is whether there is a way to pass the variant array of field values from tblSource to tblDestination using AppendRecord.
Instead of this:
// Add one record
tblDestination.Append;
for i := 0 to tblSource.FieldCount-1 do
tblDestination.Fields[i+1].Value := tblSource.Fields[i].Value;
if bSomethingCondition then
tblDestination.Fields(iSomethingConditionFieldIndex).Value := 'Something';
Do this:
tblDestination.AppendRecord({tblSource fields var array);
if bSomethingCondition then
tblDestination.Fields(iSomethingConditionFieldIndex).Value := 'Something';
Of course, it might be that there is no answer, and that the method I currently employ is the best solution.
Try the following
Query1.Open();
dxMemData1.AddFieldsFromDataSet(Query1);
dxMemData1.Open;
dxMemData1.LoadFromDataSet(Query1);
Creating an array of TVarRec isn't difficult, and AppendRecord may indeed help. The following code adds a record to a TClientDataSet (named CDS for brevity) that has 4 fields of type string, float, boolean, and string in that order:
CDS.AppendRecord(['Smith', 123.45, False, 'Test text']);
Note that you have to create a value for every single field (column) in the dataset, in the order that they exist in the FieldDefs collection, or you'll get an exception.
(Of course, the real question is why you're returning extra rows and iterating through them, instead of testing the conditions in your SQL statement WHERE clause and only returning the rows you actually need. This can almost always be done using parameters.)
If you don't want to use AddFieldsFromDataSet -> LoadFromDataSet , and want to load fields and records manually, you also can do it. You can create fields programmatically and append records by iterations. Look example:
MD.Fields.Clear;
MD.FieldDefs.Clear;
MD.Close;
with MD.FieldDefs.AddFieldDef do
begin
Name := 'UserID';
DataType := TFieldType.ftInteger;
CreateField(MD);
Name := 'GridName';
DataType := TFieldType.ftString;
Size := 255;
CreateField(MD);
Name := 'TemplateGrid';
DataType := TFieldType.ftBlob;
CreateField(MD);
end;
MD.Close;
MD.Open;
MD.Append;
MD.FieldByName('UserID').AsInteger := 1;
MD.FieldByName('GridName').AsString := Self.Name
TBlobField(MD.FieldByName('TemplateGrid')).LoadFromStream(LStream);
MD.Post;
You can easily modify it to load fields with names and types from your dataset as they are.
Dont know how to formulate this exactly so bear with me please... I am saving text from a memo to a database with date selected in the PlannerCalendar1. Since I can select multiple dates in the PlannerCalendar1, how can I post the value of the memo to all dates selected in the PlannerCalendar1?So when I click 'save' the contents of the memo gets saved to all selected dates.Database is SQLite. The table also has an ID field which is autoinc (primary).PlannerCalendar is from the set of TMS components.
procedure TForm1.cxButton1Click(Sender: TObject);
var i:integer;
begin
with UniQuery1 do
begin
UniQuery1.SQL.Text:='INSERT INTO LOG (DATE,PERSON,DONE,TIME) VALUES (:a1,:a2,:a3,:a4)';
UniQuery1.PARAMS.ParamByName('A1').VALUE := PlannerCalendar1.Date;
UniQuery1.PARAMS.ParamByName('A2').VALUE := cxmemo1.Lines.text ;
UniQuery1.PARAMS.ParamByName('A3').VALUE := (0);
UniQuery1.PARAMS.ParamByName('A4').Value := AdvOfficeStatusBar1.Panels[0].Text;
UniQuery1.ExecSQL;
cxmemo1.Clear;
UniTable1.Refresh;
Tried this at the end but it wont work :
with plannercalendar1.Dates do
begin
for i := 0 to -1 do
begin
UniQuery1.PARAMS.ParamByName('A1').VALUE :=plannercalendar1.dates.Add + i ;
UniQuery1.ExecSQL;
end;
I have no idea what a PlannerCalendar is, but presumably there's some way to get at the list of dates that are selected. You want to do something like this:
UniQuery1.SQL.Text:='INSERT INTO LOG (DATE,PERSON,DONE,TIME) VALUES (:a1,:a2,:a3,:a4)';
UniQuery1.PARAMS.ParamByName('A2').VALUE := cxmemo1.Lines.text ;
UniQuery1.PARAMS.ParamByName('A3').VALUE := (0);
UniQuery1.PARAMS.ParamByName('A4').Value := AdvOfficeStatusBar1.Panels[0].Text;
for i := 0 to PlannerCalendar1.NumberOfDatesSelected-1 do begin
UniQuery1.PARAMS.ParamByName('A1').VALUE := PlannerCalendar1.SelectedDate[i];
UniQuery1.ExecSQL;
end;
Of course, NumberOfDatesSelected and SelectedDate are wild guesses. You'll need to find out what they're really called.
You need to use the Planner's SelectionToAbsTime method :-
Var
lStart, lEnd : TDateTime;
Begin
Planner1.SelectionToAbsTime(lStart, lEnd);
For I := Trunc(lStart) To Trunc(lEnd) Do
SaveMemosForDate(I);
End;
I am getting an Syntax Error when processing the following lines of code. Especially on the AQ_Query.Open;
procedure THauptfenster.Button1Click(Sender: TObject);
var
option: TZahlerArray;
begin
option := werZahlte;
AQ_Query.Close;
AQ_Query.SQL.Clear;
AQ_Query.SQL.Add('USE wgwgwg;');
AQ_Query.SQL.Add('INSERT INTO abrechnung ');
AQ_Query.SQL.Add('(`datum`, `titel`, `betrag`, `waldemar`, `jonas`, `ali`, `ben`)');
AQ_Query.SQL.Add(' VALUES ');
AQ_Query.SQL.Add('(:datum, :essen, :betrag, :waldemar, :jonas, :ali, :ben);');
AQ_Query.Parameters.ParamByName('datum').Value := DateToStr(mcDatum.Date);
AQ_Query.Parameters.ParamByName('essen').Value := ledTitel.Text;
AQ_Query.Parameters.ParamByName('betrag').Value := ledPreis.Text;
AQ_Query.Parameters.ParamByName('waldemar').Value := option[0];
AQ_Query.Parameters.ParamByName('jonas').Value := option[1];
AQ_Query.Parameters.ParamByName('ali').Value := option[2];
AQ_Query.Parameters.ParamByName('ben').Value := option[3];
AQ_Query.Open;
end;
The error:
I am using MySQL Delphi 2010.
USE and INSERT are two different SQL
commands.
MySQL does not support so
called "Batches".
=> you have to call these commands one-by-one
Looks to me like you're using backticks on the third AQ_Query.SQL.Add line, when you need to use normal single quotes.
Use the database in the sql script.
AQ_Query.SQL.Add('INSERT INTO wgwgwg.dbo.abrechnung ');
AQ_Query.SQL.Add('(`wgwgwg.dbo.abrechnung.datum`, `wgwgwg.dbo.abrechnungtitel`, `wgwgwg.dbo.abrechnungbetrag`, `wgwgwg.dbo.abrechnungwaldemar`, `wgwgwg.dbo.abrechnungjonas`, `wgwgwg.dbo.abrechnungali`, `wgwgwg.dbo.abrechnungben`)');
AQ_Query.SQL.Add(' VALUES ');
AQ_Query.SQL.Add('(:datum, :essen, :betrag, :waldemar, :jonas, :ali, :ben);');
AQ_Query.Parameters.ParamByName('datum').Value := DateToStr(mcDatum.Date);
AQ_Query.Parameters.ParamByName('essen').Value := ledTitel.Text;
AQ_Query.Parameters.ParamByName('betrag').Value := ledPreis.Text;
AQ_Query.Parameters.ParamByName('waldemar').Value := option[0];
AQ_Query.Parameters.ParamByName('jonas').Value := option[1];
AQ_Query.Parameters.ParamByName('ali').Value := option[2];
AQ_Query.Parameters.ParamByName('ben').Value := option[3];
You can also make a new connection to wgwgwg and refer your AQ_Query to the new connection