Insert NULL with ADO from Delphi to SQL Server 2000 - delphi

I am trying to insert data into a SQL Server 2000 database with the help of ADO in Delphi 2007.
So far I've written something like this:
insert into (a,b)
select :a,:b
qry.parameters.parambyname('a').value := '';
qry.parameters.parambyname('b').value := '';
The error I get is:
Disallowed implicit conversion from data type text to data type nvarchar...
use the convert function to run this query.
The columns in the table allow NULL values.
What I want is that the SQL Server table shows NULL where the value is ''. How can I achieve that?

you can replace with theses lines
qry.parameters.parambyname('a').value := Null;
qry.parameters.parambyname('b').value := Null;

qry.parameters.parambyname('a').value := unassigned;

Try using:
qry.parameters.parambyname('a').value := Null;
qry.parameters.parambyname('b').value := Null;
or
do not set parameters at all - by default parameters are set to "Null".
Null is a special type of Variant (uses Variants), which defines an empty value (has no type).

qry.parameters.parambyname('a').AsVariant:= Null;
must be
<TAdoDataSet>.FieldByName('a').AsVariant:= Null;

Try
qry.parameters.parambyname('a').Clear;

Do not write null; it is nil:
<TAdoDataSet>.FieldByName('a').AsVariant:= Nil;

add in uses --> Variants;
after you can replace with theses lines
qry.parameters.parambyname('a').value := Null;
qry.parameters.parambyname('b').value := Null;

Use an ADOQuery, Write a "Select" for your table. I use that a lot.
Select * From MyTable Where 0=1
And double click on the ADOquery component. A field list window will be appeared. Add all fields (right click). This way you can use your fields within your Delphi code.
MyTable.Open;
MyTable.Append;
MyTableAFieldFromMyTable.AsInteger := 0;
MyTableAStringFieldFromMyTable.AsString := 'Hello, World';
MyTable.Post;
When it comes to your question:
MyTableAnotherFieldFromMyTable.Clear;
will set the value to NULL as we know it in SQL server.
Edit: I just checked a working code. I clear the field Video from a table in following code.
Ornek.Open;
Ornek.Edit;
OrnekVideo.Clear;
Query is just "select * from Ornek Where 0=1" and I do the assignments in Delphi code using fields' .AsInteger, .AsString etc properties.

Related

TStringGrid show (bcd) in delphi live binding

I connect TFDQuery with TStringGrid in live binding in delphi firemonkey apps.
I tried to use filter in TFDQuery based on Editbox for searching purpose, and it's work just fine.
but, whenever I clear the Editbox, one of my row in TStringGrid would show "(bcd)" as it's value like the pict below.
what am I doing wrong ? how can I fix it ?
Edit :
Im using mySql database with firedac tfdconnection + tfdquery
the datatype of the column is AnsiString & FmtBCS(32,0)
Im using live binding feature in delphi.
my filter code
with MainQuery do begin
Filtered := False;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
I Insert to the table with TFDConnection.execSQL
the "(BCD)" part always change on the selected Row as the pict below.
EDIT 2:
To Reproduce my error, you can :
add TStringGrid.
Add Editbox.
add tfdconnection
add tfdquery
use live binding from tfdquery to tstringgrid.
add query to tfdquery.sql.text that using SUM() in mysql. Example : "select id, sum(stock) as total_stock from stocks"
activate that tfdquery
add onkeyup event on editbox.
add this code :
FilterValue:= 'garmines_id LIKE ''/' +Edit1.Text+'%'' ESCAPE ''/'' ';
with FDQuery1 do begin
Filtered:= false;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
run
try to type something on editbox to make sure filter works fine.
clear editbox, then the "(BCD)" is show on the selected row.
I reproduce this error. this is the SS :
Well, I still don't know what exactly causing this problem but I found the work around solution that avoid this problem appears.
you need to set TStringGrid.selected value to -1 before refreshing the TFDQuery. so the code become :
FilterValue:= 'garmines_id LIKE ''/' +Edit1.Text+'%'' ESCAPE ''/'' ';
StringGrid1.selected := -1;
with FDQuery1 do begin
Filtered:= false;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
I suspect that the cause of this problem is data type that come from mysql sum() method namely FmtBCD(32)
Go to DataMapping Rules (firedac connection)
Mark ignore inherited rules
create 2 new rules
rule1: source: dtBCD / target datatype: dtDouble / all min: 0 / all max: 100
rule2: source: dtfmtbcd / target datatype: dtDouble / all min: 0 / all max: 100
click ok. now the fields will be dtDouble, and are compatible with tgrid

How to read a BLOB(Text) field using TpFIBQuery components from FibPlus

I am using Delphi 7 with the FibPlus components . One of them being TpFIBQuery.
I am loading data from a table with the generic
select * from TableName where Key = 1
One of the fields returned is of type BLOB(Text).
I can't seem to get the value into a string list informatie using either of the following 3 ways :
Informatie.Text := FieldByName('Informatie').AsString // Returns the string 'BLOB'
Informatie.Text := BlobAsString('Informatie') // Returns ''
BlobToStrings('Informatie',Informatie) // Returns ''
I have confirmed using Database Workbench that the field in the table indeed contains the saved text.
Anyone ?
usualy, i do like this
var
sl: TStrings; // blob IS NOT string!
ms: TMemoryStream;
begin
sl := TStringList.Create;
ms := TMemoryStream.Create;
try
q.FieldByName('x').SaveToStream(ms);
ms.Position := 0;
sl.LoadFromStream(ms);
// do what ever you want with sl here
// and here too
finally
sl.Free;
ms.Free;
end; // try..finally
end;
note that q is your TpFibQuery object.
also
select * from table is bad bad practice.
that habit eventually will lead you continuous headache.
After trying the solution of #jiang, which produced the same error, I finally have found the culprit.
Turns out it was an error due to my part ( it usually is, you just have to find it ).
Turns out I had set the read transaction to False sometime during the processing/reading of the fields of the original query.
I perform a lookup in another table to get the description of a integer value in the query.
This lookup query uses the same read transaction , and sets this transaction to False after looking up the description.
AFter returning to the original query, reading integer and string fields pose no problem (although the read transaction has been set to False), but reading a BLOB field into a string with the ...AsString method produces an error or returns 'BLOB'.
Obviously I need to set the read transaction to True at the start of the read actions and set it to False after ALL read transactions. This is the major change when you convert a Paradox BDE application to a Firebird of Sqlserver application.
Anyway, I am glad I have found the solution. Hopefully it will help others too.

Firebird TIBQuery insert with returning ... INTO

I have a firebird 2.x database with Generator and a trigger to generate the key field.
I need to get the returned value from below query.
INSERT INTO XXXX (vdate,description) values ('"+ VDate +"','"+ Description +"') returning vno INTO :ParamVoucherNo
I tried several versions of below code but it dont wrok and I get
Dynamic sql error sql error code = -104
Is it really possible to get the return value in delphi using TIBQuery ?
Query1->SQL->Clear();
Query1->SQL->Add("INSERT INTO XXXX (vodate,description) values ('"+ VDate +"','"+ Description +"') returning vno INTO :ParamVoucherNo");
Query1->Params->ParamByName("ParamVoucherno")->ParamType = ptResult;
Query1->Params->ParamByName("ParamVoucherno")->DataType = ftInteger;
Query1->Params->ParamByName("ParamVoucherno")->Value = "";
Query1->Prepare();
Query1->ExecSQL();
Any suggestions?
From Firebird README.returning:
The INTO part (i.e. the variable list) is allowed in PSQL only (to
assign local variables) and rejected in DSQL.
As IBX uses DSQL, you should exclude INTO part from your query.
INSERT ... RETURNING for DSQL looks the same as a call of a stored procedure, which returns result set. So, you have to use Open instead of ExecSQL.
Your mixing of dynamic SQL with parameters is just confusing.
Do this instead:
Query1->SQL->Clear();
Query1->SQL->Add("INSERT INTO table1 (vodate,description) VALUES"+
"(:VoDate,:Description) RETURNING vno INTO :VoucherNo ");
Query1->Params->ParamByName("VoDate")->Value = VDate;
Query1->Params->ParamByName("description")->Value = Description;
Query1->Prepare();
Query1->ExecSQL();
VoucherNo = Query1->Params->ParamByName("VoucherNo")->AsInteger;
Using Delphi 6 I have the ID returning successfully using an EXECUTE BLOCK statement:
EXECUTE BLOCK
RETURNS ( DeptKey INT )
AS
BEGIN
INSERT INTO DEPARTMENT
( COMPANY_KEY, DEPARTMENT_NAME )
VALUES ( 1, 'TEST1' ) RETURNING DEPARTMENT_KEY INTO :DeptKey;
SUSPEND;
END;
From Delphi you can do the folliowing:
FQuery.SQL.Text := '<Execute Block Statement>';
FQuery.Open();
ANewKey := FQuery.Fields[0].AsInteger;
IBX is not Firebird ready
you can take a look at FIBPLUS who support Firebird features
FIBPlus also supports FB2.0 insert ... into ... returning. Now you
should not bother about getting generator values from the client but
leave them in the trigger. You can also use RDB$DB_KEY. New possible
variants of work with insert returning and RDB$DB_KEY are shown in the
example “FB2InsertReturning”.
Why not get the next value for VoucherNo first, followed by
"INSERT INTO table1 (vno, vodate,description) VALUES (:VoucherNo,:VoDate,:Description)");
?
Your trigger can then either be dispensed with (which is nice), or modified to detect null (or <= zero can be useful too) and only then populate the vno field.
create trigger bi_mytable
active before insert position 1
on mytable
as
begin
if (new.vno is null)
then new.vno = next value for gen_VoucherNos;
end
Client-side you can :
select gen_id(gen_VoucherNos, 1) from rdb$database;
By modifying the trigger in this manner you save yourself a headache later on if/when you want to insert blocks of records
I wonder if that INSERT can be wrapped into EXECUTE BLOCK command.
Would IBX manage EXECUTE BLOCK then?
http://www.firebirdsql.org/refdocs/langrefupd20-execblock.html
http://firebirdsql.su/doku.php?id=execute_block
Hope to try it in both IBX and Unified Interbase in XE2
PS: Even if it does not, I found the library, that tells to work on top of IBX of Delphi XE2 (both x86 and x64) and to add EXECUTE BLOCK support: http://www.loginovprojects.ru/index.php?page=ibxfbutils#eb.
As I know there should be some changes to IBX made. Internally INSERT ... RETURNING should be treated the same way as a selectable procedure with returning parameters.
i know this question was answered a long time ago, but i must write this as clear as possible, for those who need this as i was.
i too, needed the "INSERT..RETURNING" thing.
the Delphi drove me crazy for a long time, until i changed my Data access components.
i even moved from Delphi XE2, to XE5 only because of that...
conclusion : IBX does NOT support RETURNING!
FireDAC is PERFECT for what i need with Firebird.
just move to FireDAC and you'll be able to do everything you need, and with high performance.
If you have a table with this 2 Fields: GRP_NO and GROUPNAME and you want to get the new GRP_NO you have to use RET_ as prefix, see example:
procedure TFormDatenbank.Button1Click(Sender: TObject);
var
q: Uni.TUniQuery;
ID: Integer;
GroupName: String;
begin
GroupName := 'MyGroupName';
q := TUniQuery.Create(nil);
try
q.Connection := Datenmodul.UniConnection;
q.ParamCheck := true; // the default value of ParamCheck is true.
q.SQL.Clear;
q.SQL.Add('SELECT GRP_NO, GROUPNAME FROM GROUPDATA WHERE GROUPNAME = :GROUPNAME');
q.ParamByName('GROUPNAME').AsString := GroupName;
q.Open;
if q.RecordCount > 0 then
ID := q.FieldByName('GRP_NO').AsInteger
else
begin
// there exist no group with this name, so insert this new name
q.SQL.Clear;
q.SQL.Add('INSERT INTO GROUPDATA');
q.SQL.Add('(GROUPNAME)');
q.SQL.Add('VALUES');
q.SQL.Add('(:GROUPNAME)');
q.SQL.Add('RETURNING GRP_NO;');
q.ParamByName('GROUPNAME').AsString := GroupName;
q.Execute;
ID := q.ParamByName('RET_GRP_NO').AsInteger;
end;
finally
q.Free;
end;
end;
From the IBx2 sources, you can do it like this:
//Uses IBSql;
//var Cur: IResults;
IBSQL1.SQL.Text := 'delete from tbl_document where id = 120 returning id;';
IBSQL1.Prepare;
if IBSQL1.Prepared then
begin
Cur := IBSQL1.Statement.Execute(IBTransaction1.TransactionIntf);
WriteLn(Cur.Data[cou].AsString);
Cur.GetTransaction.Commit(True);
end;
IResults interface Code:
IResults = interface
function getCount: integer;
function GetTransaction: ITransaction;
function ByName(Idx: String): ISQLData;
function getSQLData(index: integer): ISQLData;
procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PChar);
procedure SetRetainInterfaces(aValue: boolean);
property Data[index: integer]: ISQLData read getSQLData; default;
property Count: integer read getCount;
end;
Test enviroment:
Arch Linux X86
Firebird 3
Lazarus 1.9
FPC 3.0.4
Quick note: This works on new Firebird API in the IBX, But I didn't test it in Legacy Firebird API with the IBX.

Cursor not returned from Query

I'm using Delphi XE and FireBird 2.5.
Try use a TSQLStoredProc and give me the error "Cursor not returned from Query" when I put the Active property to TRUE.
An dummy example of storedproc
CREATE PROCEDURE NEW_PROCEDURE
RETURNS(
RDO SMALLINT)
AS
BEGIN
Rdo = 5;
/* Procedure body */
SUSPEND;
END;
As a workaround, a query like SELECT * FROM NEW_PROCEDURE should work (using TSQLQuery).
I think you are supposed to use the ExecProc method instead of Open / Active. Setting Active to true should only be used if your SQL Statement returns a ResultSet (a set of records), which yours doesn't.
Regards,
Stefaan

Are unique indices on Access text fields always case insensitive?

I created an MS Access table using the following code:
tbl := Database.CreateTableDef('English', 0, '', '');
try
fld := tbl.CreateField('ID', dbLong, 0);
fld.Attributes := dbAutoIncrField + dbFixedField;
tbl.Fields.Append(fld);
fld := tbl.CreateField('Content', dbText, 255);
fld.Required := true;
fld.AllowZeroLength := false;
tbl.Fields.Append(fld);
Database.TableDefs.Append(tbl);
idx := tbl.CreateIndex('PrimaryKey');
idx.Fields.Append(idx.CreateField('ID', EmptyParam, EmptyParam));
idx.Primary := True;
idx.Unique := true;
tbl.Indexes.Append(idx);
idx := tbl.CreateIndex('IX_Content');
idx.Fields.Append(idx.CreateField('Content', EmptyParam, EmptyParam));
idx.Primary := false;
idx.Unique := true;
tbl.Indexes.Append(idx);
finally
tbl := nil;
end;
This works fine until I try to insert the two strings 'Field type' and 'Field Type' into this table. I get an error telling me that the unique index restricts me from doing that. As you can see they only differ in the case of the second word. Since I did not explicitly make the index case insensitive (I wouldn't even know how to do that), I don't quite understand why this happens. Are indices on text fields always case insensitive in MS Access? If not, what am I doing wrong?
Access Jet databases are fundamentally case insensitive. That is your problem. As far as I know there is no way to make an Access index case sensitive.
Use Binary field
Case insensivity problem in Microsoft Access was long time ago addressed in article KB244693 published by Microsoft and still can be found in Web archive.
Basically, the solution is to add a Binary field into your MS Access table and that one is finally case sensitive (uses Unicode for storing binary content) and still can be used as a text field with operators =, LIKE etc.
The field of type Binary cannot be added via the UI, but you add it to your existing table using SQL statement like this:
ALTER TABLE Table1
ADD COLUMN BinaryField1 BINARY(50)
Then you can manage it normally via the Access UI.

Resources