FireDAC, Array DML, SQL Server и IDENTITY_INSERT - PullRequest
1 голос
/ 27 октября 2019

Я пытаюсь экспортировать данные в SQL Server, используя функцию Firedac Array DML. В таблице назначения есть столбец IDENTITY, и мне нужно указать для него явное значение.

Но мой запрос не выполняется со следующим сообщением об ошибке:

Состояние SQL:23000. Собственный код: 544. Сообщение: [Microsoft] [Собственный клиент SQL Server 11.0] [SQL Server] Невозможно вставить явное значение для столбца идентификаторов в таблице dic_cities, если для параметра IDENTITY_INSERT установлено значение OFF.

Таблица назначения определяется следующим образом:

CREATE TABLE dbo.dic_cities (
  id int IDENTITY(1,1) PRIMARY KEY,
  city_name varchar(50) NOT NULL
)

Мой код:

MyFDQuery.Connection.ExecSQL('SET IDENTITY_INSERT dbo.dic_cities ON');  

MyFDQuery.SQL.Text := 'INSERT INTO dbo.dic_cities (id, city_name) VALUES (:id, :city_name)';  

//Populating parameters and preparing the query
{...}

//Execute Array DML query with batch size of 100 
MyFDQuery.Execute(100, 0);  

//Finally, set IDENTITY_INSERT off for the destination table
MyFDQuery.Connection.ExecSQL('SET IDENTITY_INSERT dbo.dic_cities OFF');  

Я должен отметить, что все работает хорошо, когда я использую обычный TFDQuery с параметрами (т.е. когдане используя функцию Array DML). Но это не работает для Array DML.

Я также использовал Array DML для нескольких других СУБД в течение многих лет способом, подобным приведенному выше, с успехом.

Итак, как использовать функцию Array DMLвставить явные значения в столбец SQL Server IDENTITY?

1 Ответ

2 голосов
/ 27 октября 2019

Обновление № 2: См. https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e652377d-0607-45ca-b4a0-274361bff85a/how-to-set-identityinsert-in-dynamic-sql?forum=transactsql

Я еще не полностью переварил его, но проблема ОП кажется очень похожей.

Я пытался сконструировать SQL для использования EXEC

  FDQuery1.SQL.Text := 'EXEC (' + ''''+ sSetIIOn + ';' + sInsert + ';' + sSetIIOff + '''' + ')';

, чтобы избежать использования sp_executesql, но, к сожалению, FD не может правильно проанализировать SQL, поэтому он выдает «аргумент вне диапазона»ошибка при настройке параметров.
Обновление: Curiouser и curiouser ...

Следующий код выполняется без ошибок на SS2014 и вставляет ожидаемые 10 строк:

const
  sEmptyTable = 'delete from dbo.identtest';
  sSetIIOn = 'set identity_insert dbo.identtest ON';
  sSetIIOff = 'set identity_insert dbo.identtest OFF';
  sSelect = 'select * from dbo.identtest';
  sInsert = 'insert dbo.identtest (ID, Name) values(%d, %s)';

procedure TForm2.TestIdentityInsert;
var
  i : Integer;
  S : String;
begin
  FDQuery1.ExecSql(sEmptyTable);
  FDQuery1.ExecSql(sSetIIOn);

  for i := 1 to 10 do begin
    S := Format(sInsert, [i, '''Name' + IntToStr(i) + '''']);
    FDQuery1.ExecSQL(S);
  end;

  FDQuery1.ExecSql(sSetIIOff);
  FDQuery1.Sql.Text := sSelect;
  FDQuery1.Open;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TestIdentityInsert;
end;

Однако , замена цикла for на

  FDQuery1.SQL.Text := sSetIIOn + ';' + sInsert + ';' + sSetIIOff;

  FDQuery1.Params.ArraySize := Rows;
  for i := 0 to Rows - 1 do begin
    FDQuery1.Params[0].AsIntegers[i] := i;
    FDQuery1.Params[1].AsStrings[i] := 'Name' + IntToStr(i);
  end;

приводит к исключению, которое вы цитируете. С помощью SSMS Profiler я проверил, что SQL, отправленный на сервер, кажется правильным (и не то, что fi корректируется слоем MDac, как это иногда бывает):

exec sp_executesql N'set identity_insert dbo.identtest ON;insert dbo.identtest values(@P1, @P2);set identity_insert dbo.identtest OFF',N'@P1 int,@P2 nvarchar(4000)',0,N'Name0' [etc, repeated 9 times]

, поэтому возникает вопрос, почемуне используете sp_executesql уважайте настройку Identity_Insert и есть ли другой способ, который делает?

...