This is pretty easy to achieve, but a bit complicated to do so it is safe with multiple clients.
What you need is a new table (for example named IndexHelper
) that actually stores the parts of the index as it should be using two columns: One has the current date properly formatted as you want it in your index and one is the current index as integer. Example:
DateString CurrentIndex
-------------------------------
20160629 13
Now you need some code that helps you get the next index value atomically, i.e. in a way that also works when more than one client try to insert at the same time without getting the same index more than once.
T-SQL comes to the rescue with its UPDATE ... OUTPUT
clause, which allows you to update a table, at the same time outputting the new values as an atomic operation, which can not be interrupted.
In your case, this statement could look like this:
DECLARE @curDay NVARCHAR(10)
DELCARE @curIndex INT
DECLARE @tempTable TABLE (theDay NVARCHAR(10), theIndex INT)
UPDATE IndexHelper SET CurrentIndex = CurrentIndex + 1 OUTPUT INSERTED.DateString, INSERTED.CurrentIndex INTO @temptable WHERE CurrentDate = <code that converts CURRENT_TIMESTAMP into the string format you want>
SELECT @curDay = theDay, @curIndex = theIndex FROM @tempTable
Unfortunately you have to go the temporary table way, as it is demanded by the OUTPUT
clause.
This increments the CurrentIndex
field in IndexHelper
atomically for the current date. You can combine both into a value like this:
DECLARE @newIndexValue NVARCHAR(15)
SET @newIndexValue = @curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, @curIndex), 2)
Now the question is: How do you handle the "go back to 01 for the next day" requirement? Also easy: Add entries into IndexHelper
for 2 days in advance with the respective date and index 0. You can do this safely everytime your code is called if you check that an entry for a day is actually missing. So for today your table might look like this:
DateString CurrentIndex
-------------------------------
20160629 13
20160630 0
20160701 0
The first call tomorrow would make this look like:
DateString CurrentIndex
-------------------------------
20160629 13
20160630 1
20160701 0
20160702 0
Wrap this up into a stored procedure that does the entire INSERT process into your original table, what you get is:
- Add missing entries for the next two days to
IndexHelper
table.
- Get the next ID atomically as described above
- Combine date string and ID from the
UPDATE
command into a single string
- Use this in the
INSERT
command for your actual data
This results in the following stored procedure you can use to insert your data:
-- This is our "work date"
DECLARE @now DATETIME = CURRENT_DATETIME
-- These are the date strings that we need
DECLARE @today NVARCHAR(10) = CONVERT(NVARCHAR, @now, 112)
DECLARE @tomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 1, @now), 112)
DECLARE @datomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 2, @now), 112)
-- We will need these later
DECLARE @curDay NVARCHAR(10)
DELCARE @curIndex INT
DECLARE @tempTable TABLE (theDay NVARCHAR(10), theIndex INT)
DECLARE @newIndexValue NVARCHAR(15)
-- Add entries for next two days into table
-- NOTE: THIS IS NOT ATOMIC! SUPPOSED YOU HAVE A PK ON DATESTRING, THIS
-- MAY EVEN FAIL! THAT'S WHY IS USE BEGIN TRY
BEGIN TRY
IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateString = @tomorrow)
INSERT INTO IndexHelper (@tomorrow, 0)
END TRY
BEGIN CATCH
PRINT 'hmpf'
END CATCH
BEGIN TRY
IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateString = @datomorrow)
INSERT INTO IndexHelper (@datomorrow, 0)
END TRY
BEGIN CATCH
PRINT 'hmpf again'
END CATCH
-- Now perform the atomic update
UPDATE IndexHelper
SET
CurrentIndex = CurrentIndex + 1
OUTPUT
INSERTED.DateString,
INSERTED.CurrentIndex
INTO @temptable
WHERE CurrentDate = @today
-- Get the values after the update
SELECT @curDay = theDay, @curIndex = theIndex FROM @tempTable
-- Combine these into the new index value
SET @newIndexValue = @curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, @curIndex), 2)
-- PERFORM THE INSERT HERE!!
...