次のフォーマット文字列を使用して列を更新するトリガーを追加する必要があります:<current_date>_<per_day_incremental_id>
、例:2015-10-01_36
。 IDは増分である必要があり、ギャップは許容されます。
私のアプローチはかなり素朴です。現在の日付と現在のシーケンス値でテーブルを作成し、その中に単一のレコードを保持します。
create table DailySequence
(
date date,
sequence int
)
insert into DailySequence values (getdate(), 1);
CREATE TRIGGER MakeHumanReadableId ON dbo.AuditMeasures
FOR INSERT
AS
DECLARE @ret int;
DECLARE @tempDate date;
DECLARE @nowDate date;
SET @nowDate = getdate();
SELECT @ret = t.sequence, @tempDate = t.date from DailySequence as t;
IF @nowDate = @tempDate
BEGIN
SET @ret = @ret + 1;
UPDATE DailySequence
SET sequence = @ret;
END
ELSE
BEGIN
SET @ret = 0;
UPDATE DailySequence
SET sequence = @ret, date = @nowDate;
END
UPDATE AuditMeasures
SET [HumanReadableId] = CAST(@nowdate AS VARCHAR(10)) + '_' + CAST(@ret AS VARCHAR(10));
FROM inserted
INNER JOIN AuditMeasures On inserted.id = AuditMeasures.id
GO
質問:
これを行う1つの潜在的な方法は次のとおりです(最後に、より良い方法を参照してください)。
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;
結果:
以上のことをすべて説明した後、プレゼンテーションレイヤーに連結できる2つの個別の列を単に維持しないのはなぜでしょうか。
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;
結果:
上記の方法は、はるかに適切にスケーリングでき、人間が読み取れるシーケンス番号の表示に柔軟性を提供します。
DailySequence
テーブルを更新する部分を簡略化できます。これの代わりに:
_select @ret = t.sequence, @tempDate = t.date from DailySequence as t;
if @nowDate = @tempDate
begin
set @ret = @ret + 1;
update DailySequence set sequence = @ret;
end
else
begin
set @ret = 0;
update DailySequence set sequence = @ret, date = @nowDate;
end
_
あなたはこれを使うことができます:
_UPDATE
dbo.DailySequence
SET
@ret = sequence = CASE date WHEN @nowDate THEN sequence + 1 ELSE 0 END,
date = @nowDate
;
_
したがって、_@ret
_変数はsequence
に格納された値を使用してUPDATEステートメントで初期化されます。
別の方法として、次のようにUPDATEを書き換えてalsoset @nowDate = getdate();
ステートメントを削除することもできます。
_UPDATE
dbo.DailySequence
SET
@ret = sequence = CASE date WHEN CAST(GETDATE() AS date) THEN sequence + 1 ELSE 0 END,
@nowDate = date = GETDATE()
;
_
または、おそらく、このようにさえ:
_UPDATE
dbo.DailySequence
SET
@ret = sequence = CASE date WHEN x.Today THEN sequence + 1 ELSE 0 END,
@nowDate = date = x.Today
FROM
(SELECT CAST(GETDATE() AS date)) AS x (Today)
;
_
このようにして、UPDATEステートメントは_@nowDate
_と_@ret
_の両方を初期化します。 _@tempDate
_変数は、どちらのオプションでも必要ありません。