Nice article! I've dealt with this problem before (I implemented a client-side solution instead of an SQL based solution). I like the approach and probably will use it in the future.
If you did want to worry about concurrency, there is always the technique of serializing the requests by obtaining a row lock on a common resource.
Imagine a new table (SERIALIZER) that has one row, start a transaction and update the row as the first action in the stored procedure. Now, other calls to the stored proc will wait on the first call to finish the transaction.
Just makes sure to commit the transaction at the end of the stored procedure (or rollback in case of an error).
If it is your first article then I must say -decently written article.
I have also dealt with such situations many times but what I noticed in your solution was that you did not handle the case if maximum possible Invoice Id (i.e. yy-99999) was already generated. However, you put enough attention not to let generate a duplicate.
Anyways, it was a good article and keep sharing.
For SQLServer Magazine subscribers, here is a somewhat related artitcle
Is there a benefit to dealing with storing and looking up the string 'yy-xxxxx', rather than 2 numerical fields (a year field and some incrementing number)?
I think this code solves the problem of concurrent inserts. One SELECT is used to generate the new invoice id, but if another user manages to sneak it in first the code loops and tries the next number.
Yes. Or possibly even a single identity column that was re-seeded each year to yy00000; the "-" could be added prior to display just for the "visual effect" for the user. "SET IDENTITY_INSERT ON" would be needed for prior year, with logic similar to above, but hopefully that would be relatively rare.
Yes, this is a much easier problem if you just use an integer identity field. You could create a view using "STUFF(CAST(invoice_id as char(8)),3,0,'-') as invoice_id" for anything that insists on seeing it that way.
I have tried for years to convince boneheads that if they want to see numbers with leading zeros, embedded dashes, etc, in their stupid spreadsheets then they should figure out how to format a column in Excel rather than try to dictate that I need to change all the numeric fields in the database to characters. If you have better luck than I do, let me know how you do it. Funny how after they run the data through Excel and lose all the leading zeroes they still want to blame the database.
The last time someone whined about storing the leading zeroes I offered to print a binary dump of the data pages to prove that every 32-bit integer field did indeed have all of the leading zero bits intact. I haven't had any complaints since then, but it's only a matter of time.
Why not just store the sequence number in a table and perform an update statement like this (assumes a table called SequenceTable exists and has two columns, CurrentSequenceNumber [int or bigint] and SequenceYear [int]):
update SequenceTableset @NewSequenceNumber = CurrentSequenceNumber + 1, CurrentSequenceNumber = @NewSequenceNumberwhere SequenceYear = year(getdate())
This gives you the next number in the sequence and updates the sequence table in a single transaction. When the new year rolls around just add a new row to the sequence table for the new year and start the sequence at zero again.
The update statement could also be executed from a user-defined function that concatenates the year and sequence number into a string.