Delphi 2007 Adoquery Parameters not working - sql

ADOQuerySelect.Close;
ADOQuerySelect.SQL.Add(' AND (дата_заказа between #'+dat+'# and #'+da+'#)');
if ComboBox6.Text <> '' then
begin
ADOQuerySelect.Parameters.ParamByName('Name').Value := ComboBox6.Text ;
ADOQuerySelect.SQL.Add(' AND (Наименование = :Name)');
end;
ADOQuerySelect.Open;
I use Delphi 2007, MS Access. And i dont now how work with parameters. On this code, i have error: parameter Name not found. I tried many other variants of code, but they all not working. I add parameter Name via GUI with datatype ftstring;

Changing SQL in your code clears the existing parameters, so there's no parameter called 'Name' at the time you try to set its value. You then create the Name parameter, but it's too late.
Change the order of your statements:
ADOQuerySelect.Close;
ADOQuerySelect.SQL.Add(' AND (дата_заказа between #'+dat+'# and #'+da+'#)');
if ComboBox6.Text <> '' then
begin
ADOQuerySelect.SQL.Add(' AND (Наименование = :Name)');
ADOQuerySelect.Parameters.ParamByName('Name').Value := ComboBox6.Text ;
end;
ADOQuerySelect.Open;
You should be using parameters for all your replacements, BTW. Let the database driver handle conversions and date formats and quoting values and all of that for you.

In the object inspector ADOQuerySelect should have 'Name' among list of parameters.
You can also use the following code to create the parameter 'Name':
with ADOQuerySelect.Parameters.AddParameter do
begin
Name := 'Name';
DataType := ftString;
end;

Related

How to execute function with parameter in PostgreSQL using anonymous code block in FireDAC?

My database (PostgreSQL 11) has a function that must be called to execute an action. The return is not important, as long as there's no error. This function has arguments that must be passed as parameters.
I cannot directly use TFDConnection.ExecSQL because I use parameters of type array that is not supported by the method (to my knowledge). So, I use TFDQuery.ExecSQL like this:
msql1: = 'DO $$ BEGIN PERFORM DoIt (:id,:items); END $$; ';
{... create FDQuery (fq), set connection, set SQL.Text to msql1}
fq.Params.ParamByName ('id'). AsInteger: = 1;
{$ REGION 'items'}
fq.ParamByName ('items'). DataType: = ftArray;
fq.ParamByName ('items'). ArrayType: = atTable; // Must be atTable, not atArray
if length (items)> 0 then
begin
fq.ParamByName ('items'). ArraySize: = length (items);
for it: = 0 to length (items) -1 do
fq.ParamByName ('items'). AsIntegers [it]: = items [it];
end;
fq.ExecSQL;
{$ ENDREGION}
When executing the above code, the error above message raises
"Parameter 'id' not found".
After some research that suggested using fq.Params.ParamByName I was also unsuccessful.
However, if you change the way the function is called to
select DoIt (:id,:items); and obviously replacing the execution with fq.Open works perfectly.
Is it possible to execute a PL / pgSQL block that contains parameters in the function called by this block using TFDConnection / TFDQuery?
PS: I'm using Delphi Rio 10.3.3
When you assign a value to TFDQuery.SQL, FireDAC does some pre-processing of the SQL based on various options. ResourceOptions.CreateParams option controls whether parameters should be processed. This is enabled by default.
The preprocessor recognizes string literals in your SQL and doesn't try to look for the parameters in them. You used dollar quoted string constant and that's why FireDAC doesn't recognize parameters in it. Even if you add parameter manually I think that FireDAC would not bind the value.
With that said, the proper way to execute stored procedures/function is to use TFDStoredProc. You just assign StoredProcName and call its Prepare method which will retrieve procedure's metadata (parameters) from database so you don't need to set ArrayType or DataType of the parameter.
In your code you set the DataType to ftArray which is wrong, because in case of array parameter it should be set to array's element type. Anyway, by setting fq.ParamByName ('items').AsIntegers you effectively set parameter's DataType to ftInteger. All you need to do is to set ArraySize
Here's what you should do instead:
procedure DoIt(Connection: TFDConnection; ID: Integer; const Items: TArray<Integer>);
var
StoredProc: TFDStoredProc;
ParamItems: TFDParam;
Index: Integer;
begin
StoredProc := TFDStoredProc.Create(nil);
try
StoredProc.Connection := Connection;
StoredProc.StoredProcName := 'DoIt';
StoredProc.Prepare;
StoredProc.Params.ParamByName('id').AsInteger := ID;
if Length(Items) > 0 then
begin
ParamItems := StoredProc.Params.ParamByName('items');
ParamItems.ArraySize := Length(Items);
for Index := Low(Items) to High(Items) do
ParamItems.AsIntegers[Index] := Items[Index];
end;
StoredProc.ExecProc;
finally
StoredProc.Free;
end;
end;
Alternatively you can use ExecFunc to get the result of stored function.

Is possible to set filter option in SMDBGrid to CaseInsensitive?

I have SMDBGrid component with show filter bar option set to true, but filter just working in case-sensitive mode
1.Try with lower case
2.Try with upper case
I have tried to insert the code in SMDBgrid.pas like this
procedure TSMDBGrid.ApplyFilter;
var
TempCol: Integer;
begin
if (eoFilterAutoApply in ExOptions) then
begin
TempCol := LeftCol;
BeginUpdate;
try
if DataLink.Active then
begin
DataLink.DataSet.CheckBrowseMode;
DataLink.DataSet.Filtered := False;
DataLink.DataSet.OnFilterRecord := nil;
DataLink.DataSet.OnFilterRecord := OnFilterRecord;
DataLink.DataSet.FilterOptions := [foCaseInsensitive]; <-- this the inserted code
DataLink.DataSet.Filtered := not FilterIsEmpty();//True;
end;
finally
LeftCol := TempCol;
EndUpdate;
end;
end;
if Assigned(OnFilterChanged) then
OnFilterChanged(Self);
end;
But no luck, Is posible filter the record ignoring the case?
PS:
I use Delphi 2009
You may use the OnAccentStringConvert event to transform the value for filter in column before compare:
begin
Result := UpperCase(S)
end;
Looks like I cope with this problem too. Trying to find any solution for Delphi XE 10.3 community edition and wrote to author of SMDBGrid and he found workaround.
Please use SQL ADOQuery as follows.
SELECT UPPER(field) FROM your_table
then use event OnAccentStringConvert and uppercase S String as follows:
function TYourFormName.DBGrridNameAccentStringConvert(Sender: TObject; const S: string): string;
begin
Result := UpperCase(S)
end;
This works very ugly, but at least works. Or you may just create filter by yourself for every table.

Using ParseSQL Command for ADO Parameters Cause Invalid Parameter DataType

I have some SQL Command that contains a parameter such:(Note that myID has "int" type in SQL)
vSqlString :='Select * From myTable Where myID= :paramID';
and Use ParseSQL Command to execute this command:
myADOQuery.Parameters.ParseSQL(vSqlString , True);
Now myADOQuery.Parameters.ParamByName('paramID').DataType is smallint Type and it can't accept negative integer values.
I can exactly show to compiler that my Parameter[0].DataType is ftIneteger and it works properly, but what is a good solution for this problem?
My question is asked by Mr. imanShadabi Here: Using TAdoQuery.ParseSql and has resolved by user1008646
Hope this will help.
If you want to create in runtime parameters, you can use something like this:
ADOQuery1.Close;
ADOQuery1.SQL.Text := vSqlString;
ADOQuery1.Parameters.Clear;
ADOQuery1.Parameters.CreateParameter('paramID', ftInteger, pdInput, 10, vIntegerValue);
ADOQuery1.Open;
Or you can concatenate values to the query.
For example:
//For Integer values:
vSqlString: = 'Select * From myTable Where myID =' + IntToStr (vIntegerValue);
//For String values:
vSqlString: = 'Select * From myTable Where myID =' + QuotedStr (vStringValue);
//For float values:
//Be careful with this, usually in a query, the comma is separator values,
//so make sure that the decimal separator is '.'
vDS := DecimalSeparator; //I keep the value it had
DecimalSeparator := '.';
try
ADOQuery1.close;
ADOQuery1.SQL.Text := 'Select * From myTable Where myID='+FloatToStr(vFloatValue);
ADOQuery1.Open;
finally
DecimalSeparator := vDS; //Restore the value that had
end;
The third option is to set the parameters at design time.
But I think this is not what you want.

SQL Query fails on empty result

I have a function that performs a query on a SQL database with an ADO connection, it is simply designed to provide a single result for a database entry that can only have one match for a SELECT type of query (i.e. get me the x value from ID 45, where there is and can only be one ID 45 entry).
The function works fine, until I hit a query that returns no results. The query just hangs, and the application cannot continue. Here is an example query string:
'SELECT Cost FROM MaterialCost ' +
'WHERE MaterialType = ''' + 'MS' +
''' AND Thickness = ''' + '0.250' + '''';
Again this exact string will work fine until I maybe query for something that I know before hand doesn't exist, which should return null or an empty string. Here is the function:
function SelectOneQuery(AQueryString : String; AADOConnectionString : String) : String;
var
ADOQuery: TADOQuery;
begin
//Create empty ADO Query, then connect to connection string
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString:=AADOConnectionString;
ADOQuery.SQL.Clear;
ADOQuery.SQL.Add(AQueryString);
ADOQuery.ExecSQL;
ADOQuery.Open;
//Set first query result and return first result
ADOQuery.First;
if(ADOQuery.Fields.Count > 0) then begin
result:=ADOQuery.Fields[0].Value;
end
else begin
result := '';
end;
end;
I added the fields count thing, but I'm not sure if that's helping at all. Basically, if there are no result, i want result := ''
You have a few problems in your code snippet:
The main problem is that you are checking FieldCount. FieldCount will always be nonzero because it contains the number of columns your query returns, regardless of the fact your query returned records or not. One option is to check RecordCount which represents the number of rows returned but a better option is to check EOF flag.
you are leaking ADOQuery. Always use try/finally blocks to create and cleanup objects.
ExecSQL is used for queries that don't return recordsets (like INSERT and DELETE),
use Open instead
No need to use First after Open
If you use the same query over and over you are better off using parameters, as a bonus your code will be more readable.
Example:
ADOQuery.SQL.Text := 'SELECT Cost FROM MaterialCost WHERE MaterialType = :MaterialType AND Thickness = :Thickness';
ADOQuery.Parameters.ParamByname('MaterialType').Value := 'MS';
ADOQuery.Parameters.ParamByname('Thickness').Value := 0.25;
Your function code should be something like this:
function SelectOneQuery(const AQueryString, AADOConnectionString: string): string;
var
ADOQuery: TADOQuery;
begin
Result := '';
ADOQuery := TADOQuery.Create(nil);
try
ADOQuery.ConnectionString := AADOConnectionString;
ADOQuery.SQL.Text := AQueryString;
ADOQuery.Open;
if not ADOQuery.EOF then
Result := ADOQuery.Fields[0].AsString;
finally
ADOQuery.Free;
end;
end;

Type mismatch in expression in Delphi 7 on SQL append

I have a code, that checks the current date when a form is loaded, performs a simple calculation, and appends a SQL in Delphi. It works on Windows 7 with Delphi 7, and on another computer with Xp, but doesn't on 3 other computers with Xp. When the form is loaded it shows a "Type mismatch in expression", and points to the line after the append. What could be the problem?
procedure TfmJaunumi.FormCreate(Sender: TObject);
var d1, d2: TDate;
begin
d1:= Date;
d2:= Date-30;
With qrJaunumi do
begin
Open;
SQL.Append('WHERE Sanem_datums BETWEEN' + #39 + DateToStr(d1) +
#39 + 'AND' + #39 + DateToStr(d2) + #39);
Active := True;
end;
end;
As robsoft says, it is probably the internationalisation settings. You could use parameterised queries instead - they are generally simpler if using dates and times.
Also, the Open after the with's begin isn't needed - in fact it will open the query without the WHERE clause you are adding.
procedure TfmJaunumi.FormCreate(Sender: TObject);
var d1, d2: TDate;
begin d1:= Date; d2:= Date-30; With
qrJaunumi do
begin
SQL.Append('WHERE Sanem_datums BETWEEN :StartDate AND :EndDate');
// exact expression will vary according to DB connection type.
// Example is for TADOQuery.
Parameters.ParamByName('StartDate').Value := d1;
Parameters.ParamByName('EndDate').Value := d2;
Active := True;
end;
end;
You may use a prepared statement to overcome any localization problems with date time values. DateToStr depends on the client side. FormatDateTime can fail if the server's localization doesn't accept the date format.
procedure TfmJaunumi.FormCreate(Sender: TObject);
var
d1, d2: TDate;
begin
d1:= Date;
d2:= Date - 30;
//qrJaunumi.SQL.Clear; removed because it would remove the "SELECT ... FROM ..." part
qrJaunumi.SQL.Add('WHERE Sanem_datums BETWEEN :StartDate AND :StopDate ');
qrJaunumi.Prepared := True;
qrJaunumi.ParamByName('StartDate').AsDateTime := d1;
qrJaunumi.ParamByName('StopDate').AsDateTime := d2;
qrJaunumi.Open; // = qrJaunumi.Active := True;
end;
The space after ":StopDate" is important because Delphi has a bug in the parameter parser unless they fixed it in newer versions.
It's almost certainly to do with the local internationalisation settings on those computers - DateToStr will return a string in the local date format (possibly MM/DD/YYYY or DD/MM/YYYY) - and depending on where you are this might not be what you're expecting.
I suspect you'll find that the computers it's not working on think they're in a different country/use a different internationalisation setting to the computers where it's working.
A better solution would be to use FormatDateTime to get the date into a standard format that your SQL Server installation will accept, so there's no chance that any local 'internationalisation' settings can interfere like this.
Unfortunately none of the above worked, but the solution was to replace the Format with "yy.mm.dd." instead of "yyyy.mm.dd.", and adding single quotes. Weird, it says the format is "yyyy.mm.dd." everywhere.
The code looks like this now:
procedure TfmJaunumi.FormCreate(Sender: TObject);
var d1, d2: TDate; d3, d4, atd: String;
begin
d1:= Date;
d3:= FormatDateTime('yy.mm.dd.',d1);
d2:= Date-30;
d4:= FormatDateTime('yy.mm.dd.',d2);
atd := '''';
With qrJaunumi do
begin
Open;
SQL.Append('WHERE Sanem_datums BETWEEN'+ atd+d4+atd +'AND'+ atd+d3+atd+';');
Active := True;
end;
end;
Hi I had same error and found different solution from MS Support:
SQL.Text:='Delete * from TableName where((kno='+(inttostr(userNo))+')and(Sanem_datums >= # '+(FormatDateTime('mm-dd-yy',d1))+' # ))'; ExecSQL;
https://support.microsoft.com/en-us/kb/175258