I have the following problem.
When I use a SQL Select statement to filter records it either gives an error or does not show the record it should.
If the field has a ' in the value it gives an sql error,
If the value is (i'm) the error says the sintax is not correct for (i'), so the single ' cuts off the rest of the sql statement,
I have tried using code to change all the ' into " and then i do not get an error, but i do not get any records either.
Below is the code:
To convert the ' into ":
Function RestoreFromsqlFormat(SQLText:String):String;
Var
Sqlnew,sqlold:String;
Begin
sqlold:=SqlText;
if Pos('"',SQLText)<>0 then
Begin
while Pos('"',sqlold)<>0 do
Begin
SqlNew:=Copy(SqlOld,0,Pos('"',SqlOld)-1)+'''';
SqlOld:=Copy(SqlOld,Pos('"',sqlOld)+1,Length(sqlOld));
End;
End;
Result:=SQlNew+SqlOld;
End;
And when calling the sql select from statement, I am getting the following error :
Active:=False;
Sql.Text:='select*from backup_folders where (user_id='''+userID+''') and (folder='''+PreparesqlFormat('my name wouldn''t be here')+''')';
Active:=True;
May i know how to overcome this error ?
This is the wrong way to use SQL.
Get rid of your PreparesqlFormat() function and use AnsiQuotedStr() instead:
Active := False;
Sql.Text := 'select * from backup_folders where (user_id=' + AnsiQuotedStr(userID, #39) + ') and (folder=' + AnsiQuotedStr('my name wouldn''t be here', #39) + ')';
Active := True;
A better option is to use a parameterized query instead. Let the DB handle quotes for you:
Active := False;
// depending on which DB component you are using, you might need to use # instead of :
Sql.Text := 'select * from backup_folders where (user_id=:PUserID) and (folder=:PFolder)';
ParamByName('PUserID').AsString := userID;
ParamByName('PFolder').AsString := 'my name wouldn''t be here';
Active := True;
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.
We are using MSSQL 2012.
Trying to update Client photo with Stored Procedure
spui_SetClientPhoto
int ClientID
VarBinary(Max) Photo
Program runs fine with pure ADO:
ADO.ProcedureName:='spui_SetClientPhoto';
ADO.Parameters.CreateParameter('#ClientsID',ftInteger,pdInput,0,95075);
ADO.Parameters.CreateParameter('#Photo',ftBlob,pdInput,0,NULL);
ADO.Parameters[1].LoadFromFile('C:\Photo.png',ftBlob);
ADO.ExecProc;
But with CDS it cause an error:
Implicit Conversion from datatype Varchar(max) to Varbinary(max) is not allowed.
ADO.ProcedureName:='spui_SetClientPhoto';
cds.SetProvider(ADO);
cds.Params.CreateParam(ftInteger,'#ClientsID',ptInput).AsInteger:=95075;
cds.Params.CreateParam(ftBlob,'#Photo',ptInput).LoadFromFile('C:\Photo.png', ftBlob);
cds.Execute;
e.g. cannot run CDS with Parameters of BLOB type. Any solution for this?
The following works fine for me, with the Picture field type in the AdoQuery and
CDS set to ftGraphic, and the Stored Proc's DDL set to
CREATE PROCEDURE [dbo].[SetClientPhoto](#ClientID int, #Picture Image)
AS
BEGIN
SET NOCOUNT ON;
update table_2
set picture = #Picture
where
ID = #ClientID
END
Code
procedure TForm1.SavePictureViaStoredProc;
var
PrvCommandText,
PrvSql : String;
ID : Integer;
const
scTestImage = 'D:\TestPictures\TestBMP.BMP';
begin
// First, save the text of the AdoQuery's Sql and the CDS's CommandText
PrvCommandText := CDS1.CommandText;
PrvSql := AdoQuery1.SQL.Text;
// Save the iD of the row we want to use
ID := CDS1.FieldByName('ID').AsInteger;
try
// Allow CommandText changes on the DSP
DataSetProvider1.Options := DataSetProvider1.Options + [poAllowCommandText];
CDS1.Close;
// construct a Sql statement to invoke the Stored Proc
CDS1.CommandText := 'exec dbo.SetClientPhoto #ClientID = :' + IntToStr(ID) + ', #Picture = :Picture';
// Set up parameters
CDS1.Params.Clear;
CDS1.Params.CreateParam(ftInteger, '#ClientID', ptInput);
CDS1.Params.CreateParam(ftGraphic, '#Picture', ptInput);
CDS1.Params.ParamByName('#ClientID').Value := ID;
CDS1.Params.ParamByName('#Picture').LoadFromFile(scTestImage, ftGraphic);
AdoQuery1.Close;
AdoQuery1.SQL.Text := '';
CDS1.Execute; // This executes the stored proc
CDS1.Params.Clear;
finally
ADoQuery1.SQL.Text := PrvSql;
CDS1.CommandText := PrvCommandText;
CDS1.Open;
end;
end;
Note: I very rarely store images in databases, and have not yet managed to get this to work with .Jpg and .Png files. I vaguely recall that there is an extra step needed with storing those in a DB without getting a "Stream read error" or "Invalid image" exception, and I'll see if I can remind myself of it later.
Using Delphi XE2.
Building a software package which connects to a database via query/datasource.
I would like to implement a filter option for records in a table, so at a click of a button a cxgrid will then display the records which match the filter selections.
I cant quite work out how to do this. Any help will be appreciated.
Have this so far but I honestly don't if this is close to what im trying to achieve.
procedure TFilter.btnClick(Sender: TObject);
begin
with aQry do
begin
SQL.Clear;
Close;
SQL.Text := 'select * from TABLE where record_name like'+QuotedStr(name.Text+'%');
SQL.Text := 'and record_type like '+QuotedStr(type.Text+'%');
SQL.Text := 'and record_type2 like '+QuotedStr(type2.Text+'%');
SQL.Text := 'and record_type3 like '+QuotedStr(type3.Text+'%');
SQL.Text := 'and record_type4 like '+QuotedStr(type4.Text+'%');
Open;
end;
end;
In your code you are building a invalid SQL sentence, because you are overwritten the content of the SQL each time which the Text property is set, So you must use the Add method to build the SQL sentence. Also you must consider use parameters.
Try the next sample which build and run a parameterized SQL sentence depending of the values entered on the filters (maybe you will need modify the source in order to run).
AQry.Close;
AQry.SQL.Clear;
AQry.SQL.Add('select * from TABLE where 1=1');
if name.Text<>'' then
AQry.SQL.Add('and record_name like :record_name');
if Edittype.Text<>'' then
AQry.SQL.Add('and record_type like :record_type');
if type2.Text<>'' then
AQry.SQL.Add('and record_type2 like :record_type2');
if type3.Text<>'' then
AQry.SQL.Add('and record_type3 like :record_type3');
if type4.Text<>'' then
AQry.SQL.Add('and record_type4 like :record_type4');
if name.Text<>'' then
Aqry.Parameters.ParamByName('record_name').Value := name.Text+ '%';
if Edittype.Text<>'' then
Aqry.Parameters.ParamByName('record_type').Value := Edittype.Text+ '%';
if type2.Text<>'' then
Aqry.Parameters.ParamByName('record_type2').Value := type2.Text+ '%';
if type3.Text<>'' then
Aqry.Parameters.ParamByName('record_type3').Value := type3.Text+ '%';
if type4.Text<>'' then
Aqry.Parameters.ParamByName('record_type4').Value := type4.Text+ '%';
AQry.Open;
You can do what you want using the Query's Filter property. First, execute the query to get all records that could be displayed.
aQry.SQL.Text := 'select * from TABLE';
aQry.Open;
Then when the values to filter on are defined, define and activate the Filter
aQry.Filtered := false;
aQry.Filter := 'record_name like'+QuotedStr(name.Text+'%') '+
'and record_type like '+QuotedStr(type.Text+'%') '+
'and record_type2 like '+QuotedStr(type2.Text+'%') '+
'and record_type3 like '+QuotedStr(type3.Text+'%') '+
'and record_type4 like '+QuotedStr(type4.Text+'%');
aQry.Filtered := true;
If any of the Text values are changed, you will have to execute the above again.
This approach does the filtering in your program's memory, and does not refresh the data from the database. If you expect the data in the database to change between filter changes, RRUZ's approach may be better for you.
An alternative to Query's Filter, is the cxGrid.TableView.DataSource.Filter. Defining it in code is a bit more complicated than the Query's Filter. But it can be easily be defined by the user without any code if you make TableView's Navigator and Navigator.Filter visible and enabled.
i m trying to build an invoice program that holds data in an Access db. i have some tedit s, buttons, one datasource, one adotable, one dbgrid and a popup menu. database format is accdb.
Problem: i want the program to filter records while user is typing. it might filter dbgrid or tedit, doesn t matter. i somehow found some code, for example:
Table1.FilterOptions:=[foCaseInsensitive];
Table1.Filter:='Filmadi='+QuotedStr(Edit1.Text+'*');
Table1.Filtered:=true;
the code above gives this error: Project Project1.exe raised exception class eoleexception with message: Item cannot be found in the collection corresponding to the requested name or ordinal
other examples give various errors.
sincerely
onur
Use LIKE operator in your filter:
procedure DoIncrementalFilter(Dataset: TDataSet; const FieldName, SearchTerm: string);
begin
Assert(Assigned(Dataset), 'No dataset is assigned');
if SearchTerm = '' then
Dataset.Filtered := False
else
begin
Dataset.Filter := FieldName + ' LIKE ' + QuotedStr(SearchTerm + '*');
Dataset.Filtered := True;
end;
end;
Example:
DoIncrementalFilter(ADOTable1, 'Filmadi', Edit1.Text);