如何将空值显式插入参数化查询?

时间:2011-06-15 02:01:00

标签: delphi firebird

我正在使用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.

5 个答案:

答案 0 :(得分:10)

错误的原因是'dbx'不知道参数的数据类型。由于它从未被赋值,因此它的数据类型在执行时间为ftUnknown,因此出错。 'ParamType'也是一样,但默认情况下假设'ptInput',所以没问题。

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


绝对不需要Clear参数,因为它已经是NULL。我们怎么知道呢? IsNull正在回归......

来自TParam.Clear Method

  

使用“清除”为a分配NULL值   参数。

来自TParam.IsNull Property

  

指示是否指定值   参数为NULL(空白)。


绝对不需要Bound参数,因为完全无关。当'Bound'为false时,数据集将尝试从其参数的数据源提供默认值。 但您的数据集甚至没有链接到数据源。来自documentation

  

[...]表示查询的数据集   和存储过程使用的值   绑定以确定是否分配   参数的默认值。如果   绑定是假的,数据集即   表示查询尝试分配   来自数据集的值   他们的DataSource属性。 [...]

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

答案 1 :(得分:8)

使用TParam.Clear

Query.ParamByName('ThisValue').Clear;

“使用清除为参数指定NULL值。” (来自文件)

答案 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