我正在使用Delphi 7和Firebird 1.5。
我有一个在运行时创建的查询,其中某些值可能为null。我无法弄清楚如何让Firebird接受我需要保留为null的值的显式空值。在这个阶段,我正在构建SQL,以便我不包含null的参数,但这很乏味且容易出错。
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;
目前EDatabaseError“在DB.pas中引发参数'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.
答案 0 :(得分:10)
错误的原因是'dbx'不知道参数的数据类型。由于它从未被赋值,因此它的数据类型在执行时间为ftUnknown
,因此出错。 'ParamType'也是一样,但默认情况下假设'ptInput',所以没问题。
Query.ParamByName('ThisValue').DataType := ftString;
您绝对不需要Clear
参数,因为它已经是NULL
。我们怎么知道呢? IsNull
正在回归......
使用“清除”为a分配NULL值 参数。
指示是否指定值 参数为NULL(空白)。
您绝对不需要Bound
参数,因为完全无关。当'Bound'为false时,数据集将尝试从其参数的数据源提供默认值。 但您的数据集甚至没有链接到数据源。来自documentation:
[...]表示查询的数据集 和存储过程使用的值 绑定以确定是否分配 参数的默认值。如果 绑定是假的,数据集即 表示查询尝试分配 来自数据集的值 他们的DataSource属性。 [...]
如果文档不够,请参阅“sqlexpr.pas”中TCustomSQLDataSet.SetParamsFromCursor
中的代码。它是唯一一个在dbx框架中引用参数“Bound”的地方。
答案 1 :(得分:8)
答案 2 :(得分:1)
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, B, 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.
答案 3 :(得分:0)
您确定仅通过设置SQL文本创建了参数吗?
尝试
if Query.Params.count <> 0 then
// set params
.
.
无论如何为什么不制作SQL文本:
'INSERT INTO crs_edocument (EDOC_ID, LINKAGE_TYPE) VALUES (999327, NULL)';
如果您知道该值将为null ...
答案 4 :(得分:0)
将TConnection Options
上的一些属性命名为HandlingStringType /将空字符串转换为null。保持正确并假设为Query.ParamByName('ThisValue').AsString:=''
;
您可以在
TConnection.FetchOptions.FormatOptions.StrsEmpty2Null:=True