I am having an issue with an application using SQLServer 2008. My app has a class called SalesInvoice. I use ADO and VB6 for my app.
The Index value for the record is the "Sales Invoice Number" which was created as an Identity_Spec with an increment of one. It was created using SSMS. It is set as the Primary Key and is Unique. It is a bigint data type.
I have discovered in Microsoft documentation that the identity increment might get lost. (See the quote copied form the help file.)
"The identity property on a column does not guarantee the following:
Uniqueness of the value– Uniqueness must be enforced by using a PRIMARY KEY or UNIQUE constraint orUNIQUE index.
Consecutive values within a transaction– A transaction inserting multiple rows is not guaranteed to get consecutive values for the rows because other concurrent inserts might occur on the table. If values must be consecutive then the transaction should use an exclusive lock on the table or use the SERIALIZABLE isolation level.
Consecutive values after server restart or other failures–SQL Server might cache identity values for performance reasons and some of the assigned values can be lost during a database failure or server restart. This can result in gaps in the identity value upon insert. If gaps are not acceptable then the application should use its own mechanism to generate key values. Using a sequencegeneratorwith the NOCACHEoption can limit the gaps to transactions that are never committed.
Reuse of values– For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated."
There is no issue with consecutive values within a transaction, although each invoice is written out in a transaction, as it is paired with Sales Line detail records. They are linked using the Sales Invoice Number.
The issue comes in with the line " udtProps.InvoiceNumber = .Fields("Invoice Number")" as this is the property that returns the sales invoice number to the app that is used to actually print the invoice.
My problem is with the 3rd bullet point from the help file. "Consecutive values after server restart or other failures", particularly the "Other Failures" as they are not defined.
I have pasted the sub from the class that actually persists the record, below. It includes the Error handler routine. Ia particularly concerned that the case of the Database record lock conflict error might be one of the "Other Failures" described in the help file above. In at least One occasion, the invoice printed with an invoice number of 690172, but was written to the database with an invoice number of 690175.
If anybody has an in depth understanding of when the identity spec invoice number is set, returned and incremented, please help. Is it the first time the update is tried, or when the update command completes successfully?
Private Sub Save()
On Error GoTo ErrorHandler
iRecLockReTry = 0
Dim rs As ADODB.Recordset
Dim sSQL As String
Set rs = New ADODB.Recordset
sSQL = "SELECT * FROM [Sales Invoices] "
sSQL = sSQL & "WHERE [Invoice Number] = " & udtProps.InvoiceNumber
If cnCompany.State = adStateClosed Then
cnCompany.Open
End If
With rs
.ActiveConnection = cnCompany
.CursorLocation = adUseClient
.CursorType = adOpenDynamic
.LockType = adLockPessimistic
.Open sSQL
End With
If bNew Then rs.AddNew
With rs
... (Values set here)
.Update
If bNew Then
' update ID property similar to below when adding record for first time
udtProps.InvoiceNumber = .Fields("Invoice Number")
End If
.Close
End With
Set rs = Nothing
Exit Sub
ErrorHandler:
If Err.Number = otErrorDBUpdateConflict Then
' Err.Raise 61002, sClassName, "Another user has updated this record while you were looking at it. You will have to recall the record and re-enter your changes."
objError.ErrorHandle ERROR_CLASS_ID, otGeneralUpdateConflict, otErrorWarning
'The way we are handling things I can't see how we would ever get this error. But Microsoft has it in as an error so it must be possible...
ElseIf Err.Number = otErrorDBRecordLockConflict Then
While iRecLockReTry < otErrorConstRecordLockRetry
iRecLockReTry = iRecLockReTry + 1
rs.Requery
Resume
Wend
' Err.Raise 61001, sClassName, "Another User has this record Locked. Do you wish to Continue trying to update?"
objError.ErrorHandle ERROR_CLASS_ID, otGeneralRecordLockConflict, otErrorWarning
ElseIf Err.Number = otErrorDBDuplicateIndex Then
' Err.Raise 61003, sClassName, "Unable to update this record because one of the fields has a duplicate index conflict. Please resolve the issue and try again."
objError.ErrorHandle ERROR_CLASS_ID, otGeneralDuplicateIndex, otErrorWarning
Else
' Err.Raise Err.Number, sClassName & "." & "Save", Err.Description, Err.HelpFile, Err.HelpContext
objError.ErrorHandle ERROR_CLASS_ID, otGeneralUnexpectedDatabaseError, otErrorFatal
End If
With women, the TRUTH will not only NOT set you free, it will cause you a great deal of trouble.