我有一个带有Generator的firebird 2.x数据库和一个生成关键字段的触发器。我需要从下面的查询中获取返回的值。
INSERT INTO XXXX (vdate,description) values ('"+ VDate +"','"+ Description +"') returning vno INTO :ParamVoucherNo
我尝试了以下代码的几个版本,但它不起作用,我得到
动态sql错误sql错误代码= -104
是否真的可以使用TIBQuery在delphi中获取返回值?
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();
有什么建议?
来自Firebird README.returning:
INTO部分(即变量列表)仅在PSQL中允许(分配局部变量)并在DSQL中被拒绝。
由于IBX使用DSQL,您应该从查询中排除INTO
部分。
用于DSQL的INSERT ... RETURNING
看起来与调用存储过程相同,后者返回结果集。所以,你必须使用Open
而不是ExecSQL
。
从IBx2源代码中,您可以这样做:
//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接口代码:
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;
测试环境:Arch Linux X86 Firebird 3 Lazarus 1.9 FPC 3.0.4快速注释:这适用于IBX中的新Firebird API,但我没有使用IBX在Legacy Firebird API中测试它。
将动态SQL与参数混合起来只是令人困惑。
改为:
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;
使用Delphi 6我使用EXECUTE BLOCK语句成功返回ID:
EXECUTE BLOCK
RETURNS ( DeptKey INT )
AS
BEGIN
INSERT INTO DEPARTMENT
( COMPANY_KEY, DEPARTMENT_NAME )
VALUES ( 1, 'TEST1' ) RETURNING DEPARTMENT_KEY INTO :DeptKey;
SUSPEND;
END;
从Delphi您可以执行以下操作:
FQuery.SQL.Text := '<Execute Block Statement>';
FQuery.Open();
ANewKey := FQuery.Fields[0].AsInteger;
IBX不是Firebird准备好的
你可以看看支持Firebird功能的FIBPLUS
FIBPlus还支持FB2.0插入......进入...返回。现在你不应该费心从客户端获取生成器值,而是将它们保留在触发器中。您还可以使用RDB $ DB_KEY。插入返回和RDB $ DB_KEY的新工作变体显示在示例“FB2InsertReturning”中。
为什么不首先获得VoucherNo的下一个值,然后是
"INSERT INTO table1 (vno, vodate,description) VALUES (:VoucherNo,:VoDate,:Description)");
?
然后可以省略您的触发器(这很好),或者修改以检测null(或<= 0也可以是有用的),然后只填充vno字段。
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
客户端你可以:
select gen_id(gen_VoucherNos, 1) from rdb$database;
通过以这种方式修改触发器,如果/当您想要插入记录块时,可以在以后节省您的头痛
我想知道INSERT
是否可以包装成EXECUTE BLOCK
命令。那么IBX会管理EXECUTE BLOCK
吗?
希望在XE2中的IBX和Unified Interbase中尝试它
PS:即使它没有,我找到了这个库,告诉你在Delphi XE2的IBX(x86和x64)上工作并添加EXECUTE BLOCK
支持:http://www.loginovprojects.ru/index.php?page=ibxfbutils#eb。
据我所知,IBX应该有一些变化。内部INSERT ... RETURNING
的处理方式与返回参数的可选程序相同。
我知道很久以前就回答了这个问题,但我必须尽可能清楚地写出这个问题,对于那些需要我的人来说。
我也需要“INSERT..RETURNING”的东西。 Delphi让我疯了很长时间,直到我改变了我的数据访问组件。我甚至因此而从Delphi XE2转移到XE5 ......
结论:IBX不支持退货! FireDAC对于我需要的Firebird来说是完美的。
只需转移到FireDAC,您就可以完成所需的一切,并且性能卓越。
如果你有一个包含这2个字段的表:GRP_NO和GROUPNAME,并且你想获得新的GRP_NO,你必须使用RET_作为前缀,参见示例:
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;