使用序列设置主 ID 时如何将数据批量插入表中

Posted

技术标签:

【中文标题】使用序列设置主 ID 时如何将数据批量插入表中【英文标题】:How to bulk insert data into a table when the primary ID is set with a sequence 【发布时间】:2019-09-09 12:58:10 【问题描述】:

我正在尝试使用 SqlBulkCopy 将多行插入到 Id 列设置有序列的表中。序列和表格如下所示:

CREATE SEQUENCE [dbo].[MyTableId] 
 AS [int]
 START WITH 1
 INCREMENT BY 1
 MINVALUE -2147483648
 MAXVALUE 2147483647
 CACHE  10 
GO

CREATE TABLE [dbo].[MyTable](
    [Id] [int] NOT NULL,
    [SomeColumn] [int] NOT NULL,
    [AnotherColumn] [nvarchar](100) NOT NULL
  CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED 
  (
    [Id] ASC
  )
GO

ALTER TABLE [dbo].[MyTable] ADD
  CONSTRAINT [DF_MyTable_Id]
  DEFAULT (NEXT VALUE FOR [MyTableId]) FOR [Id]
GO

导入行的代码如下所示:

var table = new DataTable();

using (var adapter = new SqlDataAdapter($"SELECT TOP 0 * FROM dbo.MyTable", conn))

    adapter.Fill(table);


foreach (Data d in data)

    var row = table.NewRow();

    row["SomeColumn"] = d.someColumnValue;
    row["AnotherColumn"] = d.anotherColumnValue;

    table.Rows.Add(row);


using (var bulk = new SqlBulkCopy(conn))

    bulk.DestinationTableName = "dbo.MyTable";
    bulk.WriteToServer(table);

将数据写入服务器时失败,因为

System.InvalidOperationException: 'Column 'Id' does not allow DBNull.Value.'

我尝试从表定义中删除 Id 列,但这只是将列序号减一。我尝试将 Id 行设置为:

table.Columns["Id"].AutoIncrement = true;

但这会忽略顺序,并且在重复运行导入时,会从 0 重新开始自动递增值。

如何指示服务器使用其序列生成新的 Id 值?或者是否可以在表中创建新行之前分别为序列生成多个值?

【问题讨论】:

您是否尝试过使用SqlBulkCopyColumnMapping 来配置列列表?如果这没有帮助,想到的另一个选项是要求序列对象提前(在开始批量复制之前)生成所需数量的 ID,并提供这些 ID 以及来自客户端的其余行数据。设置批量复制的相应选项,以便插入提供的ID。 服务器是否启用了自动增量? @Arphile Autoincrement 未在该列上启用 - 它使用的是序列 @VladimirBaranov 我会看看列映射选项。知道如何提前创建一组 ID 吗? 必须SqlBulkCopy的任何重要使用使用显式列映射,否则它将按序号映射列(即表中列的物理位置),这几乎没有帮助。在这种情况下,您需要为所有列 除了 ID 添加映射,这很容易完成 (foreach (Column c in table.Columns) if ("ID".Equals(c.ColumnName)) continue; bulk.ColumnMappings.Add(c.ColumnName, c.ColumnName); )。 【参考方案1】:

如何通过SqlCommand.ExecuteScalar() 向 SQL Server 询问当前序列值。将此 SQL 语句用作命令的输入:

SELECT current_value
FROM sys.sequences
WHERE OBJECT_ID = OBJECT_ID(N'dbo.MyTableId');

然后将列属性AutoIncrementSeed设置为上一个值加一:

// ... code to get current sequence value
string sqlText = <above sql goes here>;
SqlCommand getSeqValue = new(sqlText, your_connection);
long currentSequenceValue = (long)getSeqValue.ExecuteScalar();

// Construct DataTable structure
// maybe replacing adapter.Fill(table);  
// with 
//adapter.FillSchema(table,SchemaType.Source);

// tell table to start ID on current sequence value + 1 (or AutoIncrementStep)
table.Columns["Id"].AutoIncrement = true;
table.Columns["Id"].AutoIncrementSeed = currentSequenceValue + 1;

// prepare things and bulk insert

只是一个想法,尚未测试。 :/

【讨论】:

以上是关于使用序列设置主 ID 时如何将数据批量插入表中的主要内容,如果未能解决你的问题,请参考以下文章

mybatis怎么批量插入数据库

mybatis 批量插入 怎么获取刚刚插入的数据的id集合

ACCESS数据库插入数据不成功

SQL SERVER 使用BULK Insert将txt文件中的数据批量插入表中

如何向一个oracle表中快速插入很多条数据

将MYSQL某一数据库中的多条记录批量插入到另一MYSQL数据库时,记录不存在则插入,存在则更新