C# SqlBulkCopy批量导入时主键约束冲突问题排查求助
核心问题分析
你遇到的主键重复错误,核心矛盾在于:源数据无主键值、依赖SQL Server自增生成,但服务器生成的ID却与现有数据冲突。结合你的场景(55万行批量导入、临时拆分批次可解决),可从以下方向排查:
排查方向
检查DataTable是否意外包含主键列
虽然CSV中没有主键数据,但读取CSV时可能误生成了与目标表主键同名的列(如ID),或DataTable中残留了旧数据的主键值。即使该列值为空,错误的列映射仍会干扰SQL Server的自增逻辑。验证SQL Server自增列的标识值是否异常
SQL Server的自增列(IDENTITY)若曾被手动插入过主键值,会导致标识种子值与表中实际最大ID不匹配——服务器下一次生成的ID会重复已有数据。可执行以下SQL命令验证:DBCC CHECKIDENT('dbo.MainTbl', NORESEED);若返回的
当前标识值小于当前列最大值,说明标识值已失效。排查并发写入冲突
确认导入期间是否有其他进程(如定时任务、其他业务系统)同时向MainTbl写入数据,导致主键生成冲突。检查列映射逻辑是否冗余
你当前的代码会将DataTable的所有列映射到目标表,若DataTable中存在与主键列同名的列(哪怕无数据),会触发不必要的插入逻辑,干扰自增机制。
解决建议
显式排除主键列的映射
修改列映射逻辑,只映射非主键列,避免干扰自增列的生成:private void RunSQLBulkCopy(DataTable table) { GlobalInfo.dbConn.Open(); using (SqlTransaction transaction = GlobalInfo.dbConn.BeginTransaction()) { using (SqlBulkCopy bulkCopy = new SqlBulkCopy(GlobalInfo.dbConn, SqlBulkCopyOptions.Default, transaction)) { bulkCopy.DestinationTableName = "MainTbl"; try { // 排除主键列(假设主键名为ID),仅映射非主键列 var nonPrimaryColumns = table.Columns.Cast<DataColumn>() .Where(col => col.ColumnName != "ID") .ToList(); nonPrimaryColumns.ForEach(x => bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(x.ColumnName, x.ColumnName))); bulkCopy.BatchSize = 5000; bulkCopy.WriteToServer(table); transaction.Commit(); } catch (Exception ex) { Console.WriteLine(ex.Message); transaction.Rollback(); UploadFailedTables.Add(table); } } } if (GlobalInfo.dbConn.State == ConnectionState.Open) { GlobalInfo.dbConn.Close(); } }重置自增列的标识值
若验证发现标识值异常,执行以下SQL命令重置到当前最大ID+1:DBCC CHECKIDENT('dbo.MainTbl', RESEED);优化错误定位逻辑
将BatchSize调小(如1000),并在捕获异常时记录当前批次的范围,便于定位具体冲突行;也可在导入前过滤DataTable中意外包含主键值的行(若存在)。隔离导入时段
若存在并发写入,调整导入时间到业务低峰期,或临时锁定表(需权衡性能)避免冲突。
内容的提问来源于stack exchange,提问作者Geetarman

