Um método potencial de fazer isso seria (veja o melhor método, no final):
USE tempdb;
CREATE TABLE [dbo].[tblIDs]
(
IDName nvarchar(255) NOT NULL
, LastID int NULL,
CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED
(
[IDName] ASC
) WITH
(
PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
, FILLFACTOR = 100
)
);
GO
CREATE PROCEDURE [dbo].[GetNextID](
@IDName nvarchar(255)
)
AS
BEGIN
/*
Description: Increments and returns the LastID value from
tblIDs for a given IDName
Author: Max Vernon / Mike Defehr
Date: 2012-07-19
*/
DECLARE @Retry int;
DECLARE @EN int, @ES int, @ET int;
SET @Retry = 5;
DECLARE @NewID int;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET NOCOUNT ON;
WHILE @Retry > 0
BEGIN
BEGIN TRY
UPDATE dbo.tblIDs
SET @NewID = LastID = LastID + 1
WHERE IDName = @IDName;
IF @NewID IS NULL
BEGIN
SET @NewID = 1;
INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID);
END
SET @Retry = -2; /* no need to retry since the operation completed */
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
SET @Retry = @Retry - 1;
ELSE
BEGIN
SET @Retry = -1;
SET @EN = ERROR_NUMBER();
SET @ES = ERROR_SEVERITY();
SET @ET = ERROR_STATE()
RAISERROR (@EN,@ES,@ET);
END
END CATCH
END
IF @Retry = 0 /* must have deadlock'd 5 times. */
BEGIN
SET @EN = 1205;
SET @ES = 13;
SET @ET = 1
RAISERROR (@EN,@ES,@ET);
END
ELSE
SELECT @NewID AS NewID;
END
GO
CREATE TABLE dbo.HumanReadableSequence
(
HumanReadableSequence_ID VARCHAR(20) NOT NULL
CONSTRAINT PK_HumanReadableSequence
PRIMARY KEY CLUSTERED
, SomeData VARCHAR(386) NOT NULL
);
GO
CREATE PROCEDURE dbo.HumanReadableSequence_Insert
(
@SomeData VARCHAR(386)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @NextID INT;
DECLARE @Today VARCHAR(20);
DECLARE @t TABLE
(
ID INT NOT NULL
);
SET @Today = (CONVERT(VARCHAR(20), GETDATE(), 101))
INSERT INTO @t (ID)
EXEC dbo.GetNextID @IDName = @Today;
INSERT INTO dbo.HumanReadableSequence (HumanReadableSequence_ID, SomeData)
SELECT (@Today + '_' + CONVERT(VARCHAR(20), ID, 0))
, @SomeData
FROM @t;
END
GO
EXEC dbo.HumanReadableSequence_Insert N'this is a test';
SELECT *
FROM dbo.HumanReadableSequence;
Os resultados:
Dito tudo isso, eu perguntaria por que não simplesmente manter duas colunas separadas que poderiam ser concatenadas na camada de apresentação:
CREATE TABLE dbo.HumanReadableSequence
(
CreateDate DATETIME NOT NULL
CONSTRAINT DF_HumanReadableSequence_CreateDate
DEFAULT (DATEADD(DAY, 0, DATEDIFF(DAY, 0, GETDATE())))
, HumanReadableSequence_ID INT NOT NULL
, SomeData VARCHAR(386) NOT NULL
, CONSTRAINT PK_HumanReadableSequence
PRIMARY KEY CLUSTERED
(CreateDate, HumanReadableSequence_ID)
);
DECLARE @ID INT;
DECLARE @t TABLE
(
ID INT NOT NULL
);
DECLARE @Today VARCHAR(20);
SET @Today = (CONVERT(VARCHAR(20), GETDATE(), 101))
INSERT INTO @t (ID)
EXEC dbo.GetNextID @IDName = @Today;
SELECT @ID = t.ID
FROM @t t;
INSERT INTO dbo.HumanReadableSequence (SomeData, HumanReadableSequence_ID)
VALUES ('This is a test', @ID);
SELECT HumanReadableSequenceValue =
REPLACE(CONVERT(VARCHAR(20), hrs.CreateDate, 101)
+ '_'
+ CONVERT(VARCHAR(20), hrs.HumanReadableSequence_ID, 0), '/', '-')
, SomeData
FROM dbo.HumanReadableSequence hrs;
Os resultados:
O método acima é muito mais capaz de escalar bem e oferece flexibilidade na apresentação do número de sequência legível por humanos.