Подтвердить что ты не робот

Как явным образом вставлять нули в параметризованный запрос?

Я использую Delphi 7 и Firebird 1.5.

У меня есть запрос, который я создаю во время выполнения, где некоторые из значений могут быть пустыми. Я не могу решить, как заставить 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;

В настоящее время EDatabaseError "Нет значения для параметра" ThisValue "" добавлено в DB.pas, поэтому я подозреваю, что это по дизайну, а не проблема с firebird.

Могу ли я установить параметры в NULL? Если да, то как?

(отредактируйте: извините за то, что не явным образом пробовал.Согласно раньше.Я оставил его в пользу упоминания 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.
4b9b3361

Ответ 1

Причина ошибки: "dbx" не знает тип данных параметра. Поскольку ему никогда не присваивается значение, его тип данных ftUnknown во время выполнения, следовательно, ошибка. То же самое для "ParamType", но "ptInput" принимается по умолчанию, поэтому нет проблем с этим.

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


Вам определенно не нужно Clear параметр, потому что он уже NULL. Как мы это знаем? IsNull возвращает true...

Из TParam.Clear Method:

Используйте Clear, чтобы назначить значение NULL для Параметр.

Из Свойство TParam.IsNull:

Указывает, присвоено ли значение для параметра NULL (пробел).


Вам определенно не нужно Bound параметр, поскольку он полностью неактуальен. Если "Bound" является ложным, набор данных попытается предоставить значение по умолчанию из его источника данных для параметра. Но ваш набор данных даже не связан с источником данных. В документации :

[...] Наборы данных, которые представляют запросы и хранимые процедуры используют значение Связано для определения того, следует ли назначать значение по умолчанию для параметра. Если Bound является ложным, наборы данных, которые представляют собой попытки попытаться присвоить значение из набора данных, указанного их свойство DataSource. [...]

Если документации недостаточно, обратитесь к коду в TCustomSQLDataSet.SetParamsFromCursor в 'sqlexpr.pas'. Это единственное место, где "Bound" параметра относится к инфраструктуре dbx.

Ответ 2

Используйте TParam.Clear

Query.ParamByName('ThisValue').Clear;

"Используйте Clear, чтобы назначить значение NULL для параметра." (из Документов)

Ответ 3

Ответ 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.

Ответ 4

Вы уверены, что параметры были созданы, просто установив текст SQL?

попробуйте

if Query.Params.count <> 0 then
// set params
.
.

Во всяком случае, почему бы не сделать текст SQL:

'INSERT INTO crs_edocument (EDOC_ID, LINKAGE_TYPE) VALUES (999327, NULL)';

если вы знаете, что значение будет null...

Ответ 5

Есть какое-то свойство в TConnection Options с именем HandlingStringType/Преобразование пустых строк в null. Сохраните это и предположите Query.ParamByName('ThisValue').AsString:=''; Вы можете получить к нему доступ в

TConnection.FetchOptions.FormatOptions.StrsEmpty2Null:=True