插入期间插入相同的数据

时间:2016-09-21 18:05:28

标签: sql-server vb.net tsql

我有几个插入查询,它们在事务中合并。第一个插入是创建新产品的articel数字,在表格中增加最多。不幸的是,我只是注意到mostly在测试期间,如果例如来自两个不同应用程序的两个用户点击按钮触发我的交易的方法他们可以获得相同的新产品编号。如何避免这种情况?是否有像第一次插入时锁定的东西,以便如果第一个用户访问要插入的表限制其他用户的插入,那么他们必须在第一次用户插入完成后在队列中等待?有类似的东西吗?此外我想如果有人插入其他用户无法插入。我用你要理解的代码做了评论。

以下部分交易查询:

Public Sub ProcessArticle(ByRef artikel As ArticlesVariations)
        Dim strcon = New AppSettingsReader().GetValue("ConnectionString", GetType(System.String)).ToString()
        Using connection As New SqlConnection(strcon)
            connection.Open()
            Using transaction = connection.BeginTransaction()
                Try

                    For Each kvp As KeyValuePair(Of Integer, Artikel) In artikel.collection
                        articleIndex = kvp.Key
                        Dim art As Artikel = kvp.Value

                            Using cmd As New SqlCommand("INSERT INTO tbArtikel (Nummer) VALUES (@Nummer);Select Scope_Identity()", transaction.Connection)
                                cmd.CommandType = CommandType.Text
                                cmd.Connection = connection
                                cmd.Transaction = transaction

                                 'Get next product number from table tbArtikel  (this will be new product number)'
                                 Dim NewArtNummer as String =  New DALArtikel().GetNewArtikelNumber(transaction)
                                 art.Nummer = NewArtNummer

                                cmd.Parameters.AddWithValue("@Nummer", art.Nummer)
'Get inserted product id for other diffrent inserts below'
 newArticleRowId = CInt(cmd.ExecuteScalar()) 

'....
other INSERTs queries to other tables ...
...'

       transaction.Commit()
                Catch ex As Exception
                    transaction.Rollback()
                    Throw 'Rethrow exception.'
                End Try
            End Using
        End Using
    End Sub

3 个答案:

答案 0 :(得分:2)

确保用户未分配相同值的唯一方法是在插入行时从服务器发出这些值。这是服务器为PK发布AI值的全部前提。

但是因为你的东西是一个多段,“数字字符串”会出现问题。而不是撕开字符串以找到字符串的部分上的WHERE子句的一个段的Max()+ 1。考虑这样的事情:

从用于递增和发布值的表开始:

 {DocId Int, SegmentB int, SegmentC Int}

这将只跟踪要在其他表中使用的值。然后是一个存储过程来创建/增加一个新代码(MySQL - 这是一个概念性答案):

CREATE DEFINER=`root`@`localhost` PROCEDURE `GetNextProductCode`(in docId int,
        in Minr int, 
        in Rev int 
        )
BEGIN
    SET @maxR = 0;
    SET @retCode ='';

    if Minr =-1 then
        Start transaction;
        SET @maxR = (SELECT Max(SegmentB) FROM articlecode WHERE MainId = docId) + 1;
        UPDATE articlecode SET SegmentB = @maxR WHERE MainId = docId;
        Commit;
        Select concat(Cast(docId As char) , '.', 
                      Cast(@maxR AS char) , '.',
                      Cast(Rev As char)
                      );
    end if;
END

这是一个大致的过程。因此,它只适用于第二段(我不知道当你创建一个新的SegmentB时会发生什么 - SegmentC重置为1 ???)。这个想法是:

  • 传递数字,因此无需撕掉字符串
  • 为您需要下一个
  • 值的细分传递-1
  • sp获取Max()+ 1并更新计数器表,以便下一个用户获得新值
  • 如果由于某种原因你最终没有保存该行,则会有间隙
  • sp使用一个事务(可能只需要保护更新),这样一次只能进行一次更新
  • 返回新代码。它可能只返回2个值,但无论如何你都要将它们粘在一起

有很多待办事项:

  • 只做SegmentB
  • 对于新的DocId(-1),请插入包含10001(?)默认值的新行
  • 相同的新段B(无论它是什么):为该DocId插入一个具有默认值的新行

要在之前获取新代码,请插入一行:

cmd.CommandType = CommandType.StoredProcedure

cmd.Parameters.Add("docId", MySqlDbType.Int32).Value = 3
cmd.Parameters.Add("Minr", MySqlDbType.Int32).Value = -1
cmd.Parameters.Add("Rev", MySqlDbType.Int32).Value = 1

dbcon.Open()
Using rdr = cmd.ExecuteReader()
    rdr.Read()
    Console.WriteLine(rdr(0))
End Using

明显的缺点是每个插入都需要你点击数据库以便...保存到数据库。如果它们是int值,则可以是Trigger。

答案 1 :(得分:0)

我是一名SQL开发人员,我的VB技能已经过时了大约十五年,但是不要在VB中自己创建增加的数字,而是让SQL使用IDENTITY字段生成它们。 SQL永远不会允许重复,然后你只需要返回SCOPE_IDENTITY():

ALTER TABLE dbo.tbArtikel
ADD [ArtikelID] INT IDENTITY(1,1) PRIMARY KEY;

答案 2 :(得分:0)

我有两点建议:

第一个建议:将您的代码移动到存储过程,这样您的所有用户都将执行相同的事务,您可以按照自己的方式设置隔离级别。阅读This

第二个建议:我会在您的字段Nummer上创建一个唯一索引。这样,当我尝试插入重复值时,它会引发一个错误,我可以通过告诉用户他需要重试相同的操作或自动重试它来处理它。

不建议您尝试锁定记录或操作表,但是您可以在代码项目中查看article,找到您要查找的内容。如果程序在事务中间停止,请确保提供释放所有锁的机制。