问题描述
|
我正在使用Delphi 7和Firebird 1.5。
我在运行时创建了一个查询,其中某些值可能为null。我无法弄清楚如何使Firebird接受我需要保留为null的值的显式null。在此阶段,我正在构建sql,以便不包含空参数,但这很乏味且容易出错。
var
Qry: TsqlQuery;
begin
SetConnection(Query); // sets the TsqlConnection property to a live database connection
Query.sql.Text := \'INSERT INTO SoMetable (ThisColumn) VALUES (:ThisValue)\';
Query.ParamByName(\'ThisValue\').IsNull := true; // read only,true by default
Query.ParamByName(\'ThisValue\').Clear; // does not fix the problem
Query.ParamByName(\'ThisValue\').IsNull = true; // still true
Query.ParamByName(\'ThisValue\').Bound := true; // does not fix the problem
Query.Execsql;
当前在DB.pas中引发了EDatabaseError \“参数\'ThisValue \'\” \'的无值,因此我怀疑这是设计使然,而不是火鸟问题。
我可以将参数设置为NULL吗?如果是这样,怎么办?
(编辑:抱歉,您之前没有明确尝试过.Clear。我忽略了它,只提了IsNull。添加了声明和更多代码)
抱歉,还有一件事:表上没有\“ NOT NULL \”约束。我不认为这已经走到了尽头,但我想我应该说。
完整的控制台应用程序,可在我的末端显示问题:
program InsertNull;
{$APPTYPE CONSOLE}
uses
DB,sqlExpr,Variants,SysUtils;
var
sqlConnection1: TsqlConnection;
Query: TsqlQuery;
begin
sqlConnection1 := TsqlConnection.Create(nil);
with sqlConnection1 do
begin
Name := \'sqlConnection1\';
DriverName := \'Interbase\';
GetDriverFunc := \'getsqlDriverINTERBASE\';
LibraryName := \'dbexpint.dll\';
LoginPrompt := False;
Params.clear;
Params.Add(\'Database=D:\\Database\\ZMDDEV12\\clinplus\');
Params.Add(\'RoleName=RoleName\');
//REDACTED Params.Add(\'User_Name=\');
//REDACTED Params.Add(\'Password=\');
Params.Add(\'ServerCharSet=\');
Params.Add(\'sqlDialect=1\');
Params.Add(\'BlobSize=-1\');
Params.Add(\'CommitRetain=False\');
Params.Add(\'WaitOnLocks=True\');
Params.Add(\'ErrorResourceFile=\');
Params.Add(\'LocaleCode=0000\');
Params.Add(\'Interbase TransIsolation=ReadCommited\');
Params.Add(\'Trim Char=False\');
vendorLib := \'gds32.dll\';
Connected := True;
end;
sqlConnection1.Connected;
Query := TsqlQuery.Create(nil);
Query.sqlConnection := sqlConnection1;
Query.sql.Text := \'INSERT INTO crs_edocument (EDOC_ID,LINKAGE_TYPE) VALUES (999327,:ThisValue)\';
//Query.ParamByName(\'ThisValue\').IsNull := true; // read only,true by default
// Query.ParamByName(\'ThisValue\').Value := NULL;
Query.ParamByName(\'ThisValue\').clear; // does not fix the problem
Query.ParamByName(\'ThisValue\').Bound := True; // does not fix the problem
// Query.ParamByName(\'ThisValue\').IsNull; // still true
Query.Execsql;
end.
解决方法
错误的原因是\'dbx \'不知道参数的数据类型。由于从不分配任何值,因此其执行时间的数据类型为ѭ2hence,因此会出现错误。与\'ParamType \'相同,但默认情况下假定\'ptInput \',因此没有问题。
Query.ParamByName(\'ThisValue\').DataType := ftString;
您绝对不需要Clear
参数,因为它已经是NULL
。我们怎么知道呢? IsNull
返回了真相...
从TParam.Clear方法:
使用清除将NULL值分配给
参数。
从TParam.IsNull属性:
指示是否分配了值
参数为NULL(空白)。
您绝对不需要Bound
参数,因为它完全无关紧要。如果\'Bound \'为假,则数据集将尝试从其数据源中为该参数提供默认值。但是您的数据集甚至都没有链接到数据源。从文档中:
[...]代表查询的数据集
和存储过程使用的值
一定要确定是否分配一个
参数的默认值。如果
绑定为假,数据集
表示查询尝试分配一个
数据集所指示的值
他们的DataSource属性。 [...]
如果文档不够,请参考\'sqlexpr.pas \'中的TCustomSQLDataSet.SetParamsFromCursor
中的代码。这是在dbx框架中唯一引用参数\'Bound \'的地方。
, 使用TParam.Clear
Query.ParamByName(\'ThisValue\').Clear;
\“使用清除为参数分配NULL值。\”(来自文档)
, Sertac的答案是最正确的,但我也发现驱动程序的选择会有所作为。
为了他人的利益,这是一个经过改进的测试程序,该程序演示了如何使用Firebird 1.5通过参数化查询插入空值。
program InsertNull;
{$APPTYPE CONSOLE}
uses
DB,SQLExpr,Variants,SysUtils;
var
SQLConnection1: TSQLConnection;
Query: TSQLQuery;
A,B,C: variant;
begin
SQLConnection1 := TSQLConnection.Create(nil);
Query := TSQLQuery.Create(nil);
try
try
with SQLConnection1 do
begin
Name := \'SQLConnection1\';
DriverName := \'InterXpress for Firebird\';
LibraryName := \'dbxup_fb.dll\';
VendorLib := \'fbclient.dll\';
GetDriverFunc := \'getSQLDriverFB\';
//DriverName := \'Interbase\';
//GetDriverFunc := \'getSQLDriverINTERBASE\';
//LibraryName := \'dbexpint.dll\';
LoginPrompt := False;
Params.clear;
Params.Add(\'Database=127.0.0.1:D:\\Database\\testdb\');
Params.Add(\'RoleName=RoleName\');
Params.Add(\'User_Name=SYSDBA\');
Params.Add(\'Password=XXXXXXXXXXXX\');
Params.Add(\'ServerCharSet=\');
Params.Add(\'SQLDialect=1\');
Params.Add(\'BlobSize=-1\');
Params.Add(\'CommitRetain=False\');
Params.Add(\'WaitOnLocks=True\');
Params.Add(\'ErrorResourceFile=\');
Params.Add(\'LocaleCode=0000\');
Params.Add(\'Interbase TransIsolation=ReadCommited\');
Params.Add(\'Trim Char=False\');
//VendorLib := \'gds32.dll\';
Connected := True;
end;
Query.SQLConnection := SQLConnection1;
Query.SQL.Clear;
Query.Params.Clear;
// FYI
// A is Firebird Varchar
// B is Firebird Integer
// C is Firebird Date
Query.Sql.Add(\'INSERT INTO tableX (A,C) VALUES (:A,:B,:C)\');
Query.ParamByName(\'A\').DataType := ftString;
Query.ParamByName(\'B\').DataType := ftInteger;
Query.ParamByName(\'C\').DataType := ftDateTime;
A := Null;
B := Null;
C := Null;
Query.ParamByName(\'A\').AsString := A;
Query.ParamByName(\'B\').AsInteger := B;
Query.ParamByName(\'C\').AsDateTime := C;
Query.ExecSQL;
writeln(\'done\');
readln;
except
on E: Exception do
begin
writeln(E.Message);
readln;
end;
end;
finally
Query.Free;
SQLConnection1.Free;
end;
end.
, 您确定仅通过设置SQL文本即可创建参数吗?
尝试
if Query.Params.count <> 0 then
// set params
.
.
无论如何,为什么不使SQL文本:
\'INSERT INTO crs_edocument (EDOC_ID,LINKAGE_TYPE) VALUES (999327,NULL)\';
如果您知道该值将为空...
, 在ѭ14上具有一些名为HandlingStringType的属性/将空字符串转换为null。保持真实,并假设Query.ParamByName(\'ThisValue\').AsString:=\'\'
;
您可以在
TConnection.FetchOptions.FormatOptions.StrsEmpty2Null:=True