以下の表に新しい行が追加されるたびに、シーケンス(インポート許可番号)を1 .... 20160001、20160002などから1つずつ増やし、新しい年には20170001、20170002などにリセットします。
CREATE TABLE [dbo].[tblPermits](
[ImportPermitID] [int] IDENTITY(1,1) NOT NULL,
[ImportPermitNo] [nchar](20) NULL,
[ImporterName] [int] NULL,
[Province] [varchar](50) NULL,
[LodgementDate] [datetime] NULL,
[PortofEntry] [int] NOT NULL,
[EstDateofArrival] [datetime] NULL,
[ConsignmentInvoicePONo] [varchar](50) NULL,
[OtherImportConditions] [varchar](400) NULL,
[Supplier] [int] NOT NULL,
[SupplierCountry] [varchar](50) NULL,
[CountryofOrigion] [int] NOT NULL,
CONSTRAINT [PK_tblPermits] PRIMARY KEY CLUSTERED
(
[ImportPermitID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
現在私が持っているのは以下に示すようなトリガーです。
ALTER TRIGGER [dbo].[trgPermitsInsertImportPermitNo]
ON [dbo].[tblPermits] FOR INSERT
AS
UPDATE dbo.tblPermits
SET ImportPermitNo = 'IP' + CAST(YEAR(i.LodgementDate) AS CHAR(4)) + RIGHT('000000' + CAST(i.ImportPermitID AS VARCHAR(6)), 6)
FROM dbo.tblPermits p
INNER JOIN INSERTED i ON p.ImportPermitID = i.ImportPermitID
しかし、上記のトリガーのシーケンスを新しい年に0001にリセットすることができませんでした。
新しい年にシーケンスをリセットするようにトリガーを変更するにはどうすればよいですか?
SQL Server 2012以降では、これを sequence objects を使用して実装します。
SQL Server 2008 R2の場合、欠けている機能の代わりに シーケンステーブル を使用します。この場合、各年のマスターシーケンステーブルにキーがあります。次に例を示します。
CREATE TABLE dbo.SequenceTable
(
sequence_name nvarchar(20) NOT NULL,
next_value integer NOT NULL,
CONSTRAINT [PK dbo.SequenceTable sequence_name]
PRIMARY KEY CLUSTERED (sequence_name),
);
GO
INSERT dbo.SequenceTable
(sequence_name, next_value)
VALUES
(N'PermitIDs for 2016', 1),
(N'PermitIDs for 2017', 1),
(N'PermitIDs for 2018', 1);
シーケンスからキーまたはキーの範囲を確実に割り当てる標準の割り当てストアドプロシージャは次のとおりです。
CREATE PROCEDURE dbo.Allocate_TSQL
@SequenceName nvarchar(20), -- The name of the sequence to allocate keys from
@RangeSize integer = 1, -- The number of keys to allocate
@FirstAllocated integer OUTPUT -- The first key allocated (output)
AS
BEGIN
SET XACT_ABORT ON; -- Most errors will abort the batch
SET NOCOUNT ON; -- Supress 'x row(s) affected' messages
SET ROWCOUNT 0; -- Reset rowcount
-- Validate the range size requested
IF (@RangeSize IS NULL OR @RangeSize < 1)
BEGIN
RAISERROR('@RangeSize must be a positive integer (supplied value = %i)', 16, 1, @RangeSize);
RETURN 999;
END;
-- Initialize the output parameter
SET @FirstAllocated = NULL;
-- Update the row associated with @SequenceName, returning the
-- current value, and then incrementing it by @RangeSize
UPDATE dbo.SequenceTable WITH (READCOMMITTEDLOCK)
SET @FirstAllocated = next_value,
next_value = next_value + @RangeSize
WHERE sequence_name = @SequenceName;
-- If @Allocated has a non-NULL value, we know we successfully updated a row
RETURN CASE WHEN (@FirstAllocated IS NOT NULL) THEN 0 ELSE -999 END;
END;
次に、質問の表の簡略版を考えます。
CREATE TABLE dbo.ImportPermits
(
ImportPermitID integer IDENTITY (1, 1)
CONSTRAINT [PK dbo.ImportPermits ImportPermitID]
PRIMARY KEY CLUSTERED,
ImportPermitNo nchar(12) NULL,
LodgementDate datetime NULL
);
次のトリガーを使用して、1年あたりのシーケンス番号を割り当てることができます。
CREATE TRIGGER ImportPermitsImportPermitNo
ON dbo.ImportPermits
AFTER INSERT AS
BEGIN
IF @@ROWCOUNT = 0 RETURN; -- Return immediately if no rows affected
SET XACT_ABORT, NOCOUNT ON; -- Most errors abort the batch; no row count messages
SET ROWCOUNT 0; -- Ensure all rows are visible (local to trigger)
DECLARE
@rc integer,
@FirstAllocated integer,
@RowCount integer,
@Years integer,
@SeqName nvarchar(20);
-- Count rows and distinct lodgement years in the insert set
SELECT
@RowCount = COUNT(*),
@Years = COUNT(DISTINCT(YEAR(INS.LodgementDate))),
@SeqName = N'PermitIDs for ' +
CONVERT(nchar(4), MIN(YEAR(INS.LodgementDate)))
FROM Inserted AS INS;
-- Check for multiple lodgement years (not implemented)
IF @Years > 1
BEGIN
RAISERROR('Multiple LodgementDate years are not supported.', 16, 1);
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
RETURN;
END;
-- Allocate the range of sequence numbers we will need
EXECUTE @rc = dbo.Allocate_TSQL
@SequenceName = @SeqName,
@RangeSize = @RowCount,
@FirstAllocated = @FirstAllocated OUTPUT;
IF @rc <> 0
BEGIN
RAISERROR('Sequence allocation failed.', 16, 1);
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
RETURN;
END;
-- Assign ImportPermitNo values using the sequence numbers allocated
WITH Sequenced AS
(
SELECT
IP.ImportPermitNo,
IP.LodgementDate,
Seq =
@FirstAllocated - 1 +
ROW_NUMBER() OVER (
ORDER BY IP.LodgementDate ASC)
FROM Inserted AS INS
JOIN dbo.ImportPermits AS IP
ON IP.ImportPermitID = INS.ImportPermitID
)
UPDATE Sequenced
SET Sequenced.ImportPermitNo =
N'IP' +
CONVERT(nchar(4), YEAR(Sequenced.LodgementDate)) +
RIGHT(N'000000' + CONVERT(nvarchar(11), Sequenced.Seq), 6);
END;
簡潔にするために、トリガーは単一の年からのみ行を挿入することに限定されていますが、ロジックを拡張することは難しくありません。
デモ:db <> fiddle
このテーブルの使用頻度に応じて、トランザクションを開始する、年の最終日に23:59:50(またはその頃)にスケジュールされるSQL Serverエージェントジョブを設定してみてください。値を再シードしますIDの現在の年への固定 '0001'-2016年の場合、IDは20160001であり、2017年の場合、IDは20170001です-(トランザクションを使用すると、開いているトランザクションにより、他のユーザーがテーブルに効果的にロックアウトされます)真夜中(現在の年が変更されることがわかっている場合)を待ってから、トランザクションをコミットしますか?何かのようなもの
begin transaction
declare @cmd nvarchar(500) = N'DBCC CHECKIDENT(''dbo.tblPermits'', RESEED,' + convert(varchar(4),year(sysdatetime())) + '0001)'
exec (@cmd)
waitfor time '00:00:00'
commit