Как явным образом вставлять нули в параметризованный запрос?
Я использую 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.
Ответы
Ответ 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