編集 #1: トリガー ( IF UPDATE
)、ストアド プロシージャ、および最後の 2 つの例を更新しました。
CREATE TABLE dbo.CustomSequence
(
CustomSequenceID INT IDENTITY(1,1) PRIMARY KEY,
SequenceName NVARCHAR(128) NOT NULL, -- or SYSNAME
UNIQUE(SequenceName),
RangeStart INT NOT NULL,
RangeEnd INT NOT NULL,
CHECK(RangeStart < RangeEnd),
CurrentValue INT NULL,
CHECK(RangeStart <= CurrentValue AND CurrentValue <= RangeEnd)
);
GO
CREATE TRIGGER trgIU_CustomSequence_VerifyRange
ON dbo.CustomSequence
AFTER INSERT, UPDATE
AS
BEGIN
IF (UPDATE(RangeStart) OR UPDATE(RangeEnd)) AND EXISTS
(
SELECT *
FROM inserted i
WHERE EXISTS
(
SELECT * FROM dbo.CustomSequence cs
WHERE cs.CustomSequenceID <> i.CustomSequenceID
AND i.RangeStart <= cs.RangeEnd
AND i.RangeEnd >= cs.RangeStart
)
)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR(N'Range overlapping error', 16, 1);
END
END;
GO
--TRUNCATE TABLE dbo.CustomSequence
INSERT dbo.CustomSequence (SequenceName, RangeStart, RangeEnd)
SELECT N'Red Card', 1, 50000 UNION ALL
SELECT N'Blue Card', 50001, 100000 UNION ALL
SELECT N'Green Card', 100001, 150000;
GO
-- Test for overlapping range
INSERT dbo.CustomSequence (SequenceName, RangeStart, RangeEnd)
VALUES (N'Yellow Card', -100, +100);
GO
/*
Msg 50000, Level 16, State 1, Procedure trgIU_CustomSequence_VerifyRange, Line 20
Range overlapping error
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
*/
GO
-- This procedure tries to reserve
CREATE PROCEDURE dbo.SequenceReservation
(
@CustomSequenceID INT, -- You could use also @SequenceName
@IDsCount INT, -- How many IDs do we/you need ? (Needs to be greather than 0)
@LastID INT OUTPUT
)
AS
BEGIN
DECLARE @StartTranCount INT, @SavePoint VARCHAR(32);
SET @StartTranCount = @@TRANCOUNT;
IF @StartTranCount = 0 -- There is an active transaction ?
BEGIN
BEGIN TRANSACTION -- If not then it starts a "new" transaction
END
ELSE -- If yes then "save" a save point -- see http://technet.microsoft.com/en-us/library/ms188378.aspx
BEGIN
DECLARE @ProcID INT, @NestLevel INT;
SET @ProcID = @@PROCID;
SET @NestLevel = @@NESTLEVEL;
SET @SavePoint = CONVERT(VARCHAR(11), @ProcID) + ',' + CONVERT(VARCHAR(11), @NestLevel);
SAVE TRANSACTION @SavePoint;
END
BEGIN TRY
UPDATE dbo.CustomSequence
SET @LastID = CurrentValue = ISNULL(CurrentValue, 0) + @IDsCount
WHERE CustomSequenceID = @CustomSequenceID;
IF @@ROWCOUNT = 0
RAISERROR(N'Invalid sequence', 16, 1);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @StartTranCount = 0
BEGIN
ROLLBACK TRANSACTION;
END
ELSE -- @StartTranCount > 0
BEGIN
ROLLBACK TRANSACTION @SavePoint
END
DECLARE @ErrorMessage NVARCHAR(2048), @ErrorSeverity INT, @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH;
END;
GO
SELECT * FROM dbo.CustomSequence;
GO
-- Example usage #1
DECLARE @LastID INT;
EXEC dbo.SequenceReservation
@CustomSequenceID = 1, -- Red Card
@IDsCount = 2, -- How many IDs ?
@LastID = @LastID OUTPUT;
SELECT @LastID - 2 + 1 AS [FirstID], @LastID AS [LastID];
GO
-- Example usage #2
DECLARE @LastID INT;
EXEC dbo.SequenceReservation
@CustomSequenceID = 1, -- Red Card
@IDsCount = 7, -- How many IDs ?
@LastID = @LastID OUTPUT;
SELECT @LastID - 7 + 1 AS [FirstID], @LastID AS [LastID];
SELECT * FROM dbo.CustomSequence;
GO
結果:
CustomSequenceID SequenceName RangeStart RangeEnd CurrentValue
---------------- ------------ ----------- ----------- ------------
1 Red Card 1 50000 9
2 Blue Card 50001 100000 NULL
3 Green Card 100001 150000 NULL