With Delphi 7 and SQL Server 2005 I'm trying to pass a multiline parameter (a Stringlist.text) to a TAdoQuery insert script.
The insert is successful, but when i take back data from the field, i take
Line 1 Line 2 Line 3
instead of
Line 1
Line 2
Line 3
The fieldtype in the table is nvarchar(MAX) and i can't change it to any other type, the table is not mine. I tried to change the parameter type from widestring to ftMemo, but nothing changes.
Any idea?
var
QRDestLicenze: TADOQuery;
LsLic := TStringList;
begin
LsLic := TStringList.Create;
LsLic.Add('Line 1');
LsLic.Add('Line 2');
LsLic.Add('Line 3');
QRDestLic.Parameters.FindParam('FieldName).Value := LsLic.Text;
QRDestLic.ExecSQL;
end;
I created a demo doing exactly the same thing, although using Delphi 6 and SQL Server 2008.
Memo1.Lines.Clear;
Memo1.Lines.Add('Line 1');
Memo1.Lines.Add('Line 2');
Memo1.Lines.Add('Line 3');
ADOQuery1.SQL.Text := 'INSERT INTO Absences '+
'(Employee, Date_from, Notes) '+
'VALUES (99999, ''16/04/2013'', :sNotes)';
ADOQuery1.Parameters.ParamValues['sNotes'] := Memo1.Lines.Text;
ADOQuery1.ExecSQL;
ADOQuery1.SQL.Text := 'SELECT Notes FROM Absences '+
'WHERE Employee = 99999';
ADOQuery1.Open;
Memo2.Lines.Text := ADOQuery1.FieldByName('Absence_notes').AsString;
This worked as expected, showing:
Line 1
Line 2
Line 3
in both memos.
The "Notes" field is of type VARCHAR(Max).
I left the parameter type as the default (ftString), and changed no other default settings on the TADOConnection or TADOQuery.
I was using the "Microsoft OLE DB Provider for SQL Server" as the ADO data link provider.
Could there be something else we might be able to try to reproduce your issue?
Related
I write this code :
Var Q : TFDQuery;
begin
Q := TFDQuery.Create(Self);
Q.Connection := FDConnection1;
Q.Params.CreateParam(ftString,'N',ptOutput);// Try also ptResult
Q.Params.CreateParam(ftInteger,'ID',ptInput);
Q.SQL.Text := 'SELECT NOM FROM EMPLOYEE WHERE ID_EMP = :ID';
Q.Params.ParamByName('ID').Value := 1;
Q.Active := True;
ShowMessage( VarToStr(Q.Params.ParamByName('N').Value) );
The result should be the name of the employer.
I get an error :
'N' parameter not found
How can I get the result from the Query using the parameter?
If I can't , what is the the function of :
ptOutput
ptResult
Try this code:
procedure TForm1.ExecuteQuery;
var
SQL : String;
Q : TFDQuery;
begin
SQL := 'select ''Sami'' as NOM'; // Tested with MS Sql Server backend
try
Q := TFDQuery.Create(Self);
Q.Connection := FDConnection1;
Q.Params.CreateParam(ftString, 'Nom', ptOutput);// Try also ptResult
Q.SQL.Text := SQL;
Q.Open;
ShowMessage( IntToStr(Q.ParamCount));
Caption := Q.FieldByName('Nom').AsString;
finally
Q.Free; // otherwise you have a memory leak
end;
end;
You'll see that the created parameter no longer exists once the FDQuery is opened, because FireDAC "knows" that there is nothing it can do with it.
Then, replace Q.Open by Q.ExecSQL. When that executes you get an exception
with the message
Cannot execute command returning result set.
Hint: Use Open method for SELECT-like commands.
And that's your problem. If you use a SELECT statement, you get a result set whether
you like it or not, and the way to access its contents is to do something like
Nom := Q.FieldByName('Nom').AsString
You asked in a comment what is the point of ptOutput parameters. Suppose your database has a stored procedure defined like this
Create Procedure spReturnValue(#Value varchar(80) out)
as
select #Value = 'something'
Then, in your code you could do
SQL := 'exec spReturnValue :Value'; // note the absence of the `out` qualifier in the invocation of the SP
try
Q := TFDQuery.Create(Self);
Q.Connection := FDConnection1;
Q.Params.CreateParam(ftString, 'Value', ptOutput);// Try also ptResult
Q.SQL.Text := SQL;
Q.ExecSQL;
ShowMessage( IntToStr(Q.ParamCount));
Caption := Q.ParamByName('Value').AsString;
finally
Q.Free; // otherwise you have a memory leak
end;
which retrieves the output parameter of the Stored Proc into Q's Value parameter.
There is no need to manually create parameters. Data access components are smart enough to parse the SQL string and populate the parameters collection by themselves
Also to get the result you must read the query's fields. When you call Open on a Query component, the fields collection will be populated with the fields that you specified in the SELECT [fields] SQL statement
As a side note, I advice that you use the type-safe version to get the value from a TField or TParameter object: See more here
var
q : TFDQuery;
begin
q := TFDQuery.Create(Self);
q.Connection := FDConnection1;
q.SQL.Text := 'SELECT NOM FROM EMPLOYEE WHERE ID_EMP = :ID';
q.ParamByName('ID').AsInteger := 1;
q.Open;
ShowMessage(q.FieldByName('Nom').AsString);
end;
This is my first time doing a query with parameters. (Using Delphi Seattle and FireDAC in SQL Server)
(I'm planning on using DML once I get this working.)
Why am I getting this error:
[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0]
[SQL Server]Incorrect syntax near ':'.
with this query:
procedure TForm2.Button1Click(Sender: TObject);
var
FDParam: TFDParam;
begin
FDQuery1.SQL.Text := 'CREATE TABLE TestTable (Column1 Int)';
FDQuery1.ExecSQL;
FDQuery1.SQL.Text := 'INSERT INTO TestTable (Column1) VALUES (111)';
FDQuery1.ExecSQL; // works fine
FDParam := FDQuery1.Params.Add;
FDParam.Name := 'Column1';
FDParam.DataType := ftInteger;
FDParam.Paramtype := ptInput;
FDQuery1.SQL.Text := 'INSERT INTO TestTable (Column1) VALUES(:Column1)' ;
FDQuery1.ParamByName('Column1').AsInteger := 222;
FDQuery1.ExecSQL; // FAILS
end;
Ken and Johan: thanks for your comments.
Someone here had set the connection's ResourceOptions.ParamCreate and ParamExpand to false.
Overriding that in the FDQuery eliminated the problem.
Thank you again.
I´m having trouble to fetch IDENTITY columns with ADO in Delphi XE2.
Question
The ADO Fields property doesen´t return/contain any IDENTITY columns.
Is there a property or something in the connection that will enable to fetch IDENTITY columns or is this a known bug ?
Scenario
I have a stored procedure (in SYBASE ASE), that fetch 1 row with 4 columns.
One of the column is a IDENTITY column.
In this example: ADOQuery.Fields.Count will return 3 and not 4. The IDENTITY column is missing.
Ex:
procedure TForm1.FormCreate(Sender: TObject);
var
test: string;
Password: String;
UserName: string;
ServerName : string;
NoRec: integer;
MyValue: integer;
begin
Password:='xxxx';
UserName:='yyyy';
ServerName := 'zzzzz';
ADOConnection := TADOConnection.Create(Application);
with ADOConnection do
begin
CommandTimeout := 0;
IsolationLevel := ilSerializable;
Attributes := [xaAbortRetaining];
KeepConnection := True;
LoginPrompt := False;
ConnectionString := 'Provider=ASEOLEDB.1;Password=' + Password +
';Persist Security Info=True' + ';User ID=' +
UserName + ';Data Source=' + ServerName;
end;
if not Assigned(ADOQuery) then
begin
ADOQuery := TADOQuery.Create(Application);
with ADOQuery do
begin
CommandTimeout := 0;
DisableControls;
CacheSize := 500;
Connection := ADOConnection;
CursorType := ctOpenForwardOnly;
end;
end;
ADOConnection.Open();
ADOQuery.SQL.Text:= 'sp_echo';
ADOQuery.Open;
NoRec:=ADOQuery.Fields.Count; //This will return: 3, the IDENTITY col is missing
//Trying to fetch the column this way, will fail (the field doesen´t exist):
//MyValue:=ADOQUery.FieldByName('col_1').AsInteger
end;
Here is script, table, stored procedure for this example
create table tbl_echo
(
col_1 numeric(10,0) identity,
col_2 varchar(255),
col_3 int,
col_4 int
)
insert into tbl_echo (col_2, col_3, col_4) select 'testing', 100, 200
create proc sp_echo
as
select top 1 * from tbl_echo
You have two different ways to get what you want:
You declare an output parameter in your SP and, after inserting, assign ##IDENTITY to this parameter. Execute such a SP by using a TADOStoredProc component, not a TADOQuery component. After the SP finnishes, when the control comes back to your Delphi code you just read the value of such a parameter.
You simply select ##IDENTITY, what will give you a result set with a single row with a single column containing the generated value. In this case you can use a TADOQuery, but in your Delphi code you use Open (not ExecSQL) to retrieve such a resultset and just read the value from the only field is has.
Okey, I´ve tried to use some other providers with the same result.
One simple solution for this problem is just to cast the IDENTITY column to an integer in my procedure/query. By doing that it works like expected.
create proc sp_echo
as
select top 1 cast(col_1 as int) as col_1, col_2, col_3, col_4 from tbl_echo
I am working on a Delphi XE5 Firemonkey Mobil app.
I use FireDac for connection.
Just trying to do a simple query insert into sQlite database and update the listview with the inserted info.
procedure TTabbedwithNavigationForm.Button4Click(Sender: TObject);
begin
DataModule1.qSelectCustomers.SQL.Text := 'insert into Invoice (Name) values(:newName)';
DataModule1.qSelectCustomers.ParamByName('newName').AsString := 'test';
DataModule1.qSelectCustomers.ExecSQL;
BindSourceDB1.DataSet.Refresh;
////LinkFillControlToField1.BindList.FillList;
end;
My problem is i am getting error.
error:= TFDQuery : Can not perform this operation on a closed dataset.
I have tried opening the dats set but no go.
Why will this not work ?
You can insert a record into a dataset with a select query like this:
DataModule1.qSelectCustomers.SQL.Text := 'SELECT * FROM Invoice';
DataModuel1.qSelectCustomers.Active := True;
DataModule1.qSelectCustomers.Append;
DataModule1.qSelectCustomers.FieldByName('Name').Value := 'test';
DataModule1.qSelectCustomers.Post;
How to change a generator value with dbExpress framework? I want to change generator value directly with dbExpress, without writing a stored procedure in RDBMS side or etc.
Please help me for doing that.
AFAIK DBExpress don't have any class specialized in dealing with sequences/generators.
You still can use a standard TSQLQuery to instruct the dbEngine to alter a generator value, like this:
procedure TMyDataModule.RestartMyGenerator;
begin
Q := TSQLQuery.Create;
try
Q.SQLConnection := MyConnection;
//compatible with firebird 1.x and 2.x
//Q.SQL.Text := 'set generator mygenerator to 0';
//better alternative, but compatible only with firebird 2.x
Q.SQL.Text := 'alter sequence mygenerator restart with 0';
Q.ExecSQL;
finally
Q.Free;
end;
end;
(untested code, written directly in this window)
Function with param TableName returns the next Generator value
function TDM.GenID(TableName: string): Integer;
var
Qry: TSQLQuery;
begin
Qry := TSQLQuery.Create(Self);
try
Qry.SQLConnection := SQLConnection;
Qry.SQL.Add('SELECT GEN_ID(GEN_' + TableName + '_ID' + ', 1) ' +
'FROM RDB$DATABASE');
Qry.Open;
Result := Qry.Fields[0].AsInteger;
finally
Qry.Free;
end;
end;