可以在SQL BulkCopy之后返回PrimayKey ID吗?

我正在使用C#并使用SqlBulkCopy。 我有一个问题。 我需要将质量插入到一个表中,然后将另一个质量插入到另一个表中。

这2个具有PK / FK关系。

Table A Field1 -PK auto incrementing (easy to do SqlBulkCopy as straight forward) Table B Field1 -PK/FK - This field makes the relationship and is also the PK of this table. It is not auto incrementing and needs to have the same id as to the row in Table A. 

所以这些表有一对一的关系,但我不确定如何取回质量插入所做的所有PK Id,因为我需要它们用于表B.

编辑

我可以这样做吗?

 SELECT * FROM Product WHERE NOT EXISTS (SELECT * FROM ProductReview WHERE Product.ProductId = ProductReview.ProductId AND Product.Qty = NULL AND Product.ProductName != 'Ipad') 

这应该找到刚用sql批量复制插入的所有行。 我不确定如何从中获取结果然后从SP进行质量插入。

我能看到的唯一问题是,如果用户一次只执行一条记录,并且此语句同时运行,则可能会尝试在“Product Review Table”中插入两行。

所以说我有一个用户使用手动方式,另一个用户大约在同一时间进行大规模使用。

手动方式。 1.用户提交数据2. Linq to sql产品对象被生成并填充数据并被提交。 3.此对象现在包含ProductId 4.另一个linq to sql对象是为Product review表生成的并且已插入(步骤3中的Product Id随之发送)。

群众之路。 1.用户从共享数据的用户那里获取数据。 2.抓取共享用户的所有产品行。 3.发生产品行上的SQL批量复制插入。 4.我的SP选择仅存在于Product表中的所有行,并满足其他一些条件.5。这些行会发生大量插入。

那么如果步骤3(手动方式)与步骤4(质量方式)同时发生会发生什么。 我认为它会尝试两次插入相同的行,导致主要约束执行。

在那种情况下,我会使用SqlBulkCopy插入一个临时表(即看起来像我要导入的数据,但不是主要事务表的一部分),然后在DB中INSERT / SELECT将数据移动到第一个真实表中。

现在我有两个选择,具体取决于服务器版本; 我可以对第二个真实表执行第二次INSERT / SELECT ,或者我可以使用INSERT / OUTPUT子句使用表中的标识行执行第二次插入。

例如:

  -- dummy schema CREATE TABLE TMP (data varchar(max)) CREATE TABLE [Table1] (id int not null identity(1,1), data varchar(max)) CREATE TABLE [Table2] (id int not null identity(1,1), id1 int not null, data varchar(max)) -- imagine this is the SqlBulkCopy INSERT TMP VALUES('abc') INSERT TMP VALUES('def') INSERT TMP VALUES('ghi') -- now push into the real tables INSERT [Table1] OUTPUT INSERTED.id, INSERTED.data INTO [Table2](id1,data) SELECT data FROM TMP 

如果您的应用允许,您可以添加另一列,您可以在其中存储批量插入的标识符(例如guid)。 您可以显式设置此ID。

然后在批量插入之后,您只需选择具有该标识符的行。

我遇到了同样的问题,我必须找回用SqlBulkCopy插入的行的ID。 我的ID列是一个标识列。

解:

我已经使用批量复制插入了500多行,然后使用以下查询将其选中:

 SELECT TOP InsertedRowCount * FROM MyTable ORDER BY ID DESC 

此查询返回我刚插入的行及其ID。 就我而言,我有另一个独特的专栏。 所以我选择了那个列和id。 然后使用IDictionary映射它们,如下所示:

  IDictionary mymap = new Dictionary() mymap[Name] = ID 

希望这可以帮助。

根据您的需要以及对表的控制程度,您可能需要考虑使用UNIQUEIDENTIFIER(Guids)而不是IDENTITY主键。 这会将密钥管理移到数据库之外并进入您的应用程序。 这种方法存在一些严重的权衡,因此可能无法满足您的需求。 但值得考虑。 如果您确定通过批量插入将大量数据输入到表中,那么在对象模型中管理这些键通常非常方便,而不是依赖于数据库的应用程序将其返回给您数据。

您也可以按照之前的建议采用混合方法和临时表。 使用关系的GUID将数据导入这些表,然后通过SQL语句按顺序获取整数外键并将数据泵入生产表。

我会:

  1. 打开表格上的身份插入

  2. 抓住表格最后一行的ID

  3. 循环自(int i = Id; i < datable.rows.count+1; i++)

  4. 在循环中,将datable的Id属性指定给i+1

  5. 在打开保持标识的情况下运行SQL批量插入。

  6. 关闭身份插入

我认为这是获取SQL批量插入的最安全的方法,因为它可以防止应用程序在另一个线程上执行可能导致的不匹配的ID。

免责声明 :我是C#Bulk Operations项目的所有者

该库克服了SqlBulkCopy限制并添加了输出插入标识值等灵活function。

在代码后面,它确实像接受的答案,但更容易使用。

 var bulk = new BulkOperation(connection); // Output Identity bulk.ColumnMappings.Add("ProductID", ColumnMappingDirectionType.Output); // ... Column Mappings... bulk.BulkInsert(dt); 

我的方法类似于RiceRiceBaby所描述的,除了要添加的一件重要事情是,检索Max(Id)的调用需要是事务的一部分,以及对SqlBulkCopy.WriteToServer的调用。 否则,其他人可能会在您的交易过程中插入,这会使您的ID不正确。 这是我的代码:

 public static void BulkInsert(List columnInfo, List data, string destinationTableName, SqlConnection conn = null, string idColumn = "Id") { NLogger logger = new NLogger(); var closeConn = false; if (conn == null) { closeConn = true; conn = new SqlConnection(_connectionString); conn.Open(); } SqlTransaction tran = conn.BeginTransaction(System.Data.IsolationLevel.Serializable); try { var options = SqlBulkCopyOptions.KeepIdentity; var sbc = new SqlBulkCopy(conn, options, tran); var command = new SqlCommand( $"SELECT Max({idColumn}) from {destinationTableName};", conn, tran); var id = command.ExecuteScalar(); int maxId = 0; if (id != null && id != DBNull.Value) { maxId = Convert.ToInt32(id); } data.ForEach(d => { maxId++; d.GetType().GetProperty(idColumn).SetValue(d, maxId); }); var dt = ConvertToDataTable(columnInfo, data); sbc.DestinationTableName = destinationTableName; foreach (System.Data.DataColumn dc in dt.Columns) { sbc.ColumnMappings.Add(dc.ColumnName, dc.ColumnName); } sbc.WriteToServer(dt); tran.Commit(); if(closeConn) { conn.Close(); conn = null; } } catch (Exception ex) { tran.Rollback(); logger.Write(LogLevel.Error, $@"An error occurred while performing a bulk insert into table {destinationTableName}. The entire transaction has been rolled back. {ex.ToString()}"); throw ex; } }