【问题标题】:FireDAC, Array DML, SQL Server, and IDENTITY_INSERTFireDAC、阵列 DML、SQL Server 和 IDENTITY_INSERT
【发布时间】:2019-10-27 16:19:42
【问题描述】:

我正在尝试使用 Firedac 阵列 DML 功能将数据导出到 SQL Server。在目标表中,有一个 IDENTITY 列,我需要为其添加一个显式值。

但我的查询失败并显示以下错误消息:

SQL 状态:23000。本机代码:544。消息:[Microsoft][SQL Server Native Client 11.0][SQL Server]无法插入显式值 当 IDENTITY_INSERT 设置为时,表 'dic_cities' 中的标识列 关闭。

目标表定义如下:

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 时(即不使用数组 DML 功能时),一切正常。但 Array DML 失败。

多年来,我还以类似于上面的代码的方式将 Array DML 用于其他几个 DBMS,并取得了成功。

那么,如何使用 Array DML 功能将显式值插入 SQL Server IDENTITY 列?

【问题讨论】:

  • 抛开 Array dML,您能否通过检索 FDQuery 的 OnNewRecord 事件中的下一个 Identity 值成功地将行插入到表中?
  • @MartynA 不,我没有尝试过那个活动。而且我可以在没有 Array DML 的情况下轻松管理事情。但出于导出速度的原因,我需要 Array DML。
  • 好吧,如果您的问题只是抱怨语法错误的问题,请在 Sql Server Management Studio 的查询实用程序中尝试您的 INSERT 语句。
  • @MartynA 不。我的问题是如何使用数组 DML 将显式值插入 SQL Server IDENTITY 列。我更新了我的问题以避免误解。
  • 您是否使用过 SSMS 的分析器来确切地查看服务器反对的内容,以及为什么 SET_IDENTITY 设置显然不是您指定的 ExecSQL?如果您通过使用 IF 使 INSERT 语句依赖于其当前值来测试 SET_IDENTITY 值怎么办?

标签: sql-server-2008 delphi delphi-xe7 firedac


【解决方案1】:

更新#2:https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e652377d-0607-45ca-b4a0-274361bff85a/how-to-set-identityinsert-in-dynamic-sql?forum=transactsql

我还没有完全消化它,但是 OP 的问题似乎非常相似。

我尝试构建 SQL 以使用 EXEC

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

为了避免使用 sp_executesql,但不幸的是 FD 无法正确解析 SQL,因此在设置参数时会产生“参数超出范围”错误。
更新:好奇和好奇...

以下代码在 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 似乎是正确的(而不是像有时发生的那样被 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 设置,还有其他方法吗?

【讨论】:

  • 感谢您指点我使用分析器。实际上,由于我的代码错误,没有执行“SET IDENTITY_INSERT ON”。
  • 很高兴您对它进行了排序。如您所见,我的尝试遇到了障碍。
猜你喜欢
  • 2013-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
  • 1970-01-01
  • 2022-10-21
  • 1970-01-01
相关资源
最近更新 更多