Yesterday I met similar issue. What I needed to do is to insert some fake data using SQL only. For that purpose I wrote such stored procedure:
IF OBJECT_ID('dbo.GenerateHiLoIdRange') IS NOT NULL DROP PROCEDURE dbo.GenerateHiLoIdRange GO CREATE PROCEDURE dbo.GenerateHiLoIdRange ( @IdsCount INT ) AS /* =============================================================== */ /* == Generates a range of identifiers using High/Low strategy. == */ /* =============================================================== */ BEGIN SET NOCOUNT ON; IF @IdsCount <= 0 BEGIN RAISERROR('@IdsCount should be positive. But was %d', 16, -1, @IdsCount) RETURN(-100); END DECLARE @NextHi INT DECLARE @MaxLo INT SET @MaxLo = 100 -- Hardcoded value used over all tables in a database. BEGIN TRANSACTION UpdateHiLoTable SET @NextHi = ( SELECT next_hi FROM hibernate_unique_key WITH (UPDLOCK, ROWLOCK) ) UPDATE hibernate_unique_key SET next_hi = @NextHi + 1 + (@IdsCount - 1) / @MaxLo WHERE next_hi = @NextHi COMMIT TRANSACTION UpdateHiLoTable SELECT @NextHi * @MaxLo + IntValue FROM dbo.Integers(@IdsCount - 1) END GO
Main points of procedure:
- It uses hard coded MaxLo value (100) , which is used over all entities in our application.
- Calculates new NextHi value based on requested identifiers count and updates hibernate_unique_key table in transaction.
- To return a result set containing generated identifiers procedure needs some sort of "Integers" table. In my case i simulate such table with a table-valued function.
IF OBJECT_ID('dbo.Integers') IS NOT NULL DROP FUNCTION dbo.Integers GO CREATE FUNCTION dbo.Integers ( @MaxValue INT ) RETURNS @Integers TABLE (IntValue INT NOT NULL) AS /* =============================================================== */ /* == Returns the [0, @MaxValue] range of integer values. == */ /* =============================================================== */ BEGIN IF @MaxValue > 9999 BEGIN RETURN; END; DECLARE @Digits TABLE ( Digit INT NOT NULL PRIMARY KEY ) INSERT INTO @Digits (Digit) VALUES (0) INSERT INTO @Digits (Digit) VALUES (1) INSERT INTO @Digits (Digit) VALUES (2) INSERT INTO @Digits (Digit) VALUES (3) INSERT INTO @Digits (Digit) VALUES (4) INSERT INTO @Digits (Digit) VALUES (5) INSERT INTO @Digits (Digit) VALUES (6) INSERT INTO @Digits (Digit) VALUES (7) INSERT INTO @Digits (Digit) VALUES (8) INSERT INTO @Digits (Digit) VALUES (9) INSERT INTO @Integers SELECT Number FROM (SELECT Thousands.Digit * 1000 + Hundreds.Digit * 100 + Tens.Digit * 10 + Ones.Digit AS Number FROM @Digits Thousands CROSS JOIN @Digits Hundreds CROSS JOIN @Digits Tens CROSS JOIN @Digits Ones ) Integers WHERE Number <= @MaxValue ORDER BY Number RETURN; END GONote that number of integers in a function is limited to 10000. But that is what I need.
Now I can generate identifiers in t-sql batches:
CREATE TABLE #TempId (Id INT NOT NULL) SELECT * FROM hibernate_unique_key INSERT INTO #TempId EXEC dbo.GenerateHiLoIdRange 250 SELECT Id AS Id, 'PersonName' + CAST(ROW_NUMBER() OVER(ORDER BY Id) - 1 AS VARCHAR(255)) AS PersonName FROM #TempId DROP TABLE #TempId
In my sandbox database I see such results:
1 comment:
Hello mmate nice blog
Post a Comment