Delphi 如何在参数化查询中显式插入null?

Delphi 如何在参数化查询中显式插入null?,delphi,firebird,Delphi,Firebird,我正在使用Delphi7和Firebird 1.5 我在运行时创建了一个查询,其中一些值可能为null。我无法解决如何让Firebird接受需要保留为null的值的显式null。在这个阶段,我正在构建SQL,这样我就不会包含null参数,但这很繁琐,而且容易出错 var Qry: TSQLQuery; begin SetConnection(Query); // sets the TSQLConnection property to a live database connection

我正在使用Delphi7和Firebird 1.5

我在运行时创建了一个查询,其中一些值可能为null。我无法解决如何让Firebird接受需要保留为null的值的显式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;
目前,DB.pas中出现了一个EDatabaseError“参数'ThisValue''没有值”,因此我怀疑这是出于设计,而不是firebird问题

我可以将参数设置为NULL吗?如果是,怎么做

(编辑:很抱歉没有明确说明如何尝试。之前已清除。我省略了它,以便提及IsNull。添加了声明和更多代码)

抱歉,还有一件事:表上没有“notnull”约束。我不认为会有那么大的进展,但我想我应该说

完整的控制台应用程序,在我的终端显示问题:

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.
使用


“使用Clear为参数指定空值。”(来自文档)

您确定只需设置SQL的文本即可创建参数吗

试一试


如果您知道该值将为null…

错误的原因是“dbx”不知道参数的数据类型。由于从未为其赋值,因此在执行时,其数据类型为
ftUnknown
,因此会出现错误。对于“ParamType”也一样,但是默认情况下假定为“ptInput”,因此没有问题

  Query.ParamByName('ThisValue').DataType := ftString;

您肯定不需要
清除该参数,因为它已经是
NULL
。我们怎么知道的
IsNull
正在返回true

发件人:

使用“清除”将空值指定给 参数

发件人:

指示是否指定了该值 参数的值为NULL(空白)


肯定不需要
绑定该参数,因为它是完全不相关的。当“绑定”为false时,数据集将尝试从其数据源为参数提供默认值但您的数据集甚至没有链接到数据源。从:

[…]表示查询的数据集 而存储过程使用 绑定以确定是否分配 参数的默认值。如果 绑定为false,数据集 表示尝试分配查询的查询 由指示的数据集中的值 他们的数据源属性。[……]


如果文档不足,请参阅“sqlexpr.pas”中的
TCustomSQLDataSet.SetParamsFromCursor
中的代码。它是dbx框架中唯一引用参数“绑定”的地方。

Sertac的答案是最正确的,但我也发现驱动程序的选择会有所不同

为了其他人的利益,这里有一个改进的测试程序,演示了如何使用Firebird 1.5的参数化查询插入null


在名为HandlingStringType的
t连接选项上具有某些属性/将空字符串转换为null。保持为true并假设
Query.ParamByName('ThisValue')。AsString:=''
; 您可以在中访问它

TConnection.FetchOptions.FormatOptions.StrsEmpty2Null:=True

此外,必须将
Bound
属性设置为true
Query.ParamByName('ThisValue')。Bound:=true这并不能解决问题。调用clear之前,该参数为null,并引发异常。调用它后,参数为null,并引发异常。@RRUZ:我已经尝试过了。IsNull在我调用Clear之前为true,之后更为true。错误仍然存在。我使用
TParam.DataType:=…
TParam.Clear
,和
TParam.Bound:=True
(按此顺序),它对我来说总是很好。-1因为为已分配NULL的参数分配NULL没有意义。解析SQL时,参数的“FNull”在创建时设置为true。因为在这之后它没有被赋值,所以它仍然是空的。@moz,您使用哪些组件来执行sql语句?SQlExpr.pas中抛出这一语句的行是对检查“if iFldType=fldunnown then”的响应,但是设置.DataType:=ftInteger会给出Firebird ISC错误代码:335544347-验证错误。@moz,显然你在dbexpress中遇到了一个bug。谷歌搜索实际上发现了一个类似的SQL server QC,以及其他一些与各种数据类型相关的类似bug。有些应该通过Delphi2010的更新来修复(我现在在D2010上,得到了相同的错误,当我在办公室时可以在XE上测试)
NULL
一直对我有效,但我使用了
Interbase Express
组件(即使是Firebird不支持的组件)。@Cosmin Prund:这是一堆可以找到答案的东西,非常令人放心。现在,如果我们可以更改我们使用的连接组件:(@moz)你不应该仅仅为了接受答案而接受答案。它解决了你的问题吗?如果没有,请不要接受:发布更新结果,获取更多答案。这样,社区会获得知识,你最终会解决你的问题。即使你要发布最终答案。因为如果我要改变每个参数的SQL,这是一个prett这个问题实际上发生在一个有大约20个参数的insert上(诚然,只有大约10个参数可以为空)。同样,如果paramcount=0,则查询将永远不会工作,而不仅仅是在参数为null时失败。这实际上导致了一个解决方法-当我设置数据类型时,我遇到了一个不同的错误,该错误导致我指向一个建议不同数据库驱动程序的页面。但这不是我可以在生产中使用的东西,因此我一直在修改SQL表示空参数。但是感谢您的建议,这很有帮助。@moz-I,有点理解您不想接受正确的答案,因为可能是因为您的dbx驱动程序不适合此任务,所以它对您没有帮助。但是,请不要接受错误的答案,不是吗
'INSERT INTO crs_edocument (EDOC_ID, LINKAGE_TYPE) VALUES (999327, NULL)';
  Query.ParamByName('ThisValue').DataType := ftString;
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.
TConnection.FetchOptions.FormatOptions.StrsEmpty2Null:=True