I am looking for a solution to a rather serious problem I'm facing;
I want to delete a record in a dbGrid but when I click on my coded button and confirm 'Delete', I find no immediate results. In order to find the result of the deleted record, I have to close the program and re-run it. Only then do I see that the record is deleted.
My coding looks basically as follows
procedure TfrmPunte.btnDeleteClick(Sender: TObject);
var
sName, sLeerderNo : string;
begin
with dmPunte do
begin
sLeerderNo := tblLeerder['LeerderNr'];
sName := tblLeerder['NaamVan'];
if MessageDlg('Is jy seker dat jy ' + sName + ' met Leerder Nommer ' + sLeerderNo + ' wil wis van die rekords? Neem kennis dat al die rekords van sy/haar aktiwiteite ook uitgevee sal word!', mtWarning, [mbOk, mbCancel],0) = mrOK then
tblDeelname.Open;
tblDeelname.First;
while NOT tblDeelname.Eof do
begin
if tblDeelname['LeerderNr'] = sLeerderNo then
tblDeelname.Delete;
tblDeelname.Next;
end;
tblLeerder.Delete;
tblLeerder.Active := False;
tblLeerder.Active := True;
end;
end;
I am using an ADO table connected to ADO connection connected to an ACCESS database. I do appologize, some variable names are in Afrikaans.
What should I do?
Table.refresh does not work in every environment. The safe way is
Table.Active := False;
Table.Active := True;
Be aware of a problem that the actual dataset is lost. You can store it and get back locate.
Related
I'm trying to use the FireDAC DBMS Identifiers for generating a database specific query.
I'm connecting to a MySQL-Server currently (DriverID = MySQL). I want to query either from mysql or mssql with a limit / top query.
My current statement looks like this:
SELECT
{IF MSSQL} TOP(1) {fi} `tr`.`TaxRate_Primkey`
FROM
`tbl_taxrates` AS `tr`
WHERE
`tr`.`TaxRate_TaxCodeId` = `tc`.`TaxCode_Primkey`
AND
`tr`.`TaxRate_ValidSince` <= :DATE
ORDER BY `tr`.`TaxRate_ValidSince` DESC
{IF MySQL} LIMIT 1 {fi}
Of course I'm aware, that the escaping will not be correct for mssql, but that's a different story.
When I inspect the FireDAC Monitor the preprocessed query looks like this:
SELECT
TOP(1) `tr`.`TaxRate_Primkey`
FROM
`tbl_taxrates` AS `tr`
WHERE
`tr`.`TaxRate_TaxCodeId` = `tc`.`TaxCode_Primkey`
AND
`tr`.`TaxRate_ValidSince` <= ?
ORDER BY
`tr`.`TaxRate_ValidSince` DESC
LIMIT 1
Of course this will result in error
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.`TaxRate_Primkey` FROM `tbl_taxrates` AS `tr` WHERE `tr`.`TaxRate_TaxCodeId` ' at line 1 [errno=1064, sqlstate="42000"]
since it should only add the LIMIT 1 and omit the TOP(1).
Since I only have the Delphi Community Edition 10.4, I don't have access to the MSSQL-Driver. I thought, that could cause this error and I tried with others. But also with {if FIREBIRD} and {if ADS} it was the same.
The constructor looks like this:
constructor TFireDACTenantRepository.Create(
ADBName: string;
ADBServer: string;
APort: Integer;
AUserName: string;
APassword: string;
ALogger: TLogger
);
var
oParams: TStrings;
begin
inherited Create;
FLogger := ALogger;
Self.MonitorLink := nil;
Self.MonitorBy := mbRemote;
Self.Tracing := True;
FConnection := TFDConnection.Create(nil);
oParams := TStringList.Create;
try
oParams.Add('Server=' + ADBServer);
oParams.Add('Port=' + IntToStr(APort));
oParams.Add('Database=' + ADBName);
oParams.Add('User_Name=' + AUserName);
oParams.Add('Password=' + APassword);
oParams.Add('OSAuthent=No');
FDManager.AddConnectionDef('MySQLConnectionTenant', 'MySQL', oParams);
FConnection.Params.MonitorBy := Self.MonitorBy;
FConnection.ConnectionDefName := 'MySQLConnectionTenant';
FConnection.ResourceOptions.ParamCreate := True;
FConnection.ResourceOptions.MacroCreate := True;
FConnection.ResourceOptions.ParamExpand := True;
FConnection.ResourceOptions.MacroExpand := True;
FConnection.ResourceOptions.PreprocessCmdText := True;
FConnection.ResourceOptions.EscapeExpand := True;
finally
oParams.Free;
end;
FConnection.AfterConnect := DoAfterConnect;
FConnection.AfterDisconnect := DoAfterDisconnect;
end;
My question looks a bit related to this question here
How do I use FireDAC DBMS identifiers to conditionally change SQL text
Thanks
I found the solution. If you want to use the conditional escape sequence, you also have to use the FireDAC.Phys.XXX unit for all involved databases.
Since I want to use MSSQL, I have to add FireDAC.Phys.MSSQL to the use-clause.
The units are listed here:
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Databases_(FireDAC)
Maybe this answer helps others.
I am using TIBOQuery, with "file_full_path" is a calculated field.
First, I open query to get data from database
with qryFiles do
begin
Close;
ParamByName('id_dre').Value := Self.qryListID_DRE.AsString;
ParamByName('id_use').Value := rUser.ID_USE;
Open;
end;
Then I load qrery data into TdxMemData named memFiles:
memFiles.Close;
memFiles.LoadFromDataSet(qryFiles);
memFiles.Open;
And I show value of the "file_full_path" field:
ShowMessage(memFiles.FieldByName('file_full_path').Value);
Code in OnCalcFields of the query:
procedure TfrmDocsGoodRegisterDetail.qryFilesCalcFields(DataSet: TDataSet);
begin
DataSet.FieldByName('file_full_path').Value := WideLowerCase(ExtractFileDir(rSys.PathSDK)
+ '\' + DataSet.FieldByName('id_dre').AsString
+ '\' + DataSet.FieldByName('id_dfo').AsString
+ '\' + DataSet.FieldByName('file_name').AsString);
end;
Problem is when I put "file_full_path" at the last in qrery fields order, it works fine.
But when I change that field to any other positions (not at the bottom), it return null at ShowMessage line of code.
Does anyone know why? Please help, thank you.
In my program, the user completes a form and then presses Submit. Then, a textfile or a random extension file is created, in which all the user's information is written. So, whenever the user runs the application form, it will check if the file, which has all the information, exists, then it copies the information and pastes it to the form. However, it is not working for some reason (no syntax errors):
procedure TForm1.FormCreate(Sender: TObject);
var
filedest: string;
f: TextFile;
info: array[1..12] of string;
begin
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
if FileExists(filedest) then
begin
AssignFile(f,filedest);
Reset(f);
ReadLn(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
Edit1.Text := info[1];
Edit2.Text := info[2];
ComboBox1.Text := info[3];
ComboBox5.Text := info[4];
ComboBox8.Text := info[4];
ComboBox6.Text := info[5];
ComboBox7.Text := info[6];
Edit3.Text := info[7];
Edit4.Text := info[8];
Edit5.Text := info[11];
Edit6.Text := info[12];
ComboBox9.Text := info[9];
ComboBox10.Text := info[10];
CloseFile(f);
end
else
begin
ShowMessage('File not found');
end;
end;
The file exists, but it shows the message File not found. I don't understand.
I took the liberty of formatting the code for you. Do you see the difference (before, after)? Also, if I were you, I would name the controls better. Instead of Edit1, Edit2, Edit3 etc. you could use eFirstName, eLastName, eEmailAddr, etc. Otherwise it will become a PITA to maintain the code, and you will be likely to confuse e.g. ComboBox7 with ComboBox4.
One concrete problem with your code is this line:
readln(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
You forgot to specify the file f!
Also, before I formatted your code, the final end of the procedure was missing. Maybe your blocks are incorrect in your actual code, so that ShowMessage will be displayed even if the file exists? (Yet another reason to format your code properly...)
If I encountered this problem and wanted to do some quick debugging, I'd insert
ShowMessage(BoolToStr(FileExists(filedest), true));
Exit;
just after the line
filedest := ...
just to see what the returned value of FileExists(filedest) is. (Of course, you could also set a breakpoint and use the debugger.)
If you get false, you probably wonder what in the world filedest actually contains: Well, replace the 'debugging code' above with this one:
ShowMessage(filedest);
Exit;
Then use Windows Explorer (or better yet: the command prompt) to see if the file really is there or not.
I'd like to mention an another possibility to output a debug message (assuming we do not know how to operate real debugger yet):
{ ... }
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
AllocConsole; // create console window (uses Windows module) - required(!)
WriteLn('"' + filedest + '"'); // and output the value to verify
if FileExists(filedest) then
{ ... }
could anyone help?
I've inherited some software written in Delphi 5 which allows member data and fields from a database (.ADT file) to be used merged in to word.
It works fine with all version of Word except 2010 where it won't load any documents and shows the error:
"That Method is not available on that object"
I have been told the solution is to replace the preset components OpWord and OpDataSet with Ole variants. I have done so with OpWord using:
wrdApp := CreateOleObject('Word.Application');
and the documents now load up but without any merge field data. Can anyone let me know how to extract this data from the database, as the OpDataSet seems to simply just point at the table?
Or can anyone suggest a better solution than the one I'm trying. I'm very new to Delphi so I'm in abit over my head
Edit: (Requested Info)
Sorry I have more details and code if required.
The components appear to belong to a library called OfficePartner along with TOpExcel,TOpOutlook and others.
The .doc is selected from a popup ListPane on Form30, opened and populated with merge field data from Table 4. Table 1 is the members database:
{Use Table4 as we can Set a range on it}
Table4.SetRange([Table1.FieldByName('Member Id').AsString],[Table1.FieldByName('Member Id').AsString]);
{Open Word}
OpWord1.Connected := True;
{Open the Test Document}
OpWord1.OpenDocument(DocumentDirectory + '\' + Form30.ListBox1.Items[Form30.ListBox1.ItemIndex]);
{Populate the Test Document}
OpWord1.ActiveDocument.MailMerge.OfficeModel := OpDataSetModel1;
OpWord1.ActiveDocument.PopulateMailMerge;
OpWord1.ActiveDocument.ExecuteMailMerge;
I hope this helps...
Here is a little procedure for word mail merge that I used way back for D6, it's a just snippet and you have to include in some class, I don't have Delphi anymore so can't compile to make sure that it works, anyway here it is, hope it helps:
procedure MailMergeWord;
var
WordApp: TWordApplication;
WordDoc: TWordDocument;
doc : WordDocument;
FileName: OleVariant;
xx: integer;
begin
WordApp := TWordApplication.Create(nil);
WordApp.ConnectKind := ckNewInstance;
WordDoc := TWordDocument.Create(WordApp);
FileName := 'TemplateDoc.doc';
doc := WordApp.Documents.Open(FileName,EmptyParam,EmptyParam,EmptyParam,EmptyParam
,EmptyParam,EmptyParam,EmptyParam,EmptyParam
,EmptyParam);
WordDoc.ConnectTo(Doc);
for xx := 1 to WordDoc.Fields.Count do
WordDoc.Fields.Item(xx).Result.Text := OnWordVariable(WordDoc.Fields.Item(xx).Code.Text);
WordDoc.PrintOut;
WordDoc.Free;
WordApp.Free;
end;
function OnWordVariable(varName: string): string;
begin
Result := 'Value based on variable name';
end;
I'm trying to work with the class from JosephStyons but I do get an "Invalid Index" Error on the line where the "User ID" should get set.
FRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] := edUserName.Text;
Here's my environment:
WinXP Sp3, Crystal Reports Developer XI Rel.2 SP4, Delphi 5 Update Pack 1
Any help or ideas greatly appreciated!
Thx,
Reinhard
Your value for [i] could be the culprit...I can't remember for sure but I believe the first table will be Table[1] instead of Table[0] as one would expect.
I altered my loop to use:
CrTables := CrDatabase.Tables;
for crTableObj in crTables do
You might try stepping through the table using a for loop as shown above or by starting with 1 instead of 0.
I hope this helps.
Put a break point on that line and use Evaluate/Modify.
It will return an error if you try something invalid.
Examine FRpt.Database.Tables[i] and see if it's valid for what you think are the min and max values for i.
If Tables is an array, one way to avoid that is to use ...Low(Tables) to High(Tables)
If you get your Table Ok, examine FRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] and see if it's valid.
It might be that the Item getter does not like the space embedded in "User ID". Some products need either to surround by special characters like "[User ID]", other to replace by an underscore like "User_ID"
Are you also setting the password, server name and database name?
procedure TReports.LogonToDBTables(cReport:
CrystalDecisions.CrystalReports.Engine.ReportDocument;
ConnInfo: ConnectionInfo);
var
CrDataBase: Database;
CrTables: Tables;
CrTableObj: TObject;
CrTable: Table;
CrTableLogonInfo: TableLogonInfo;
iSubReportIndex: smallint;
begin
CrDataBase := CReport.Database;
CrTables := CrDatabase.Tables;
cReport.DataSourceConnections[0].IntegratedSecurity := False;
for crTableObj in crTables do
begin
crTable := CrystalDecisions.CrystalReports.Engine.Table(crTableObj);
crTableLogonInfo := crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo := ConnInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
end;
end;
function TReports.GetConnectionInfo(): ConnectionInfo;
var
cTemp: ConnectionInfo;
begin
cTemp := ConnectionInfo.Create();
cTemp.AllowCustomConnection := True;
cTemp.ServerName := GetServerName();
cTemp.DatabaseName := GetDBName();
cTemp.UserID := GetDBUserID();
cTemp.Password := GetDBPassword();
Result := cTemp;
end;