Given the following procedure call which returns an application-managed unique integer:
DECLARE @id as integer
DECLARE @num_ids as integer
EXEC dbo.i13_get_ids 2, 1, @id output, @num_ids output
SELECT @id as N'@base_id'
I'm passing the result id onto an insert statement
insert into dbo.TBL_LOCATIONS (objectid, X_Coord, Y_Coord, Loc_Name)
values (@id,296163,3961644, 'new test')
However, that can be a bit ominous when iserting 2 or more rows, as I've done here:
DECLARE @id as integer
DECLARE @num_ids as integer
EXEC dbo.i13_get_ids 2, 1, @id output, @num_ids output
SELECT @id as N'@base_id';
insert into dbo.TBL_LOCATIONS (objectid, X_Coord, Y_Coord, Loc_Name)
values (@id,296163,3961644, 'new test');
EXEC dbo.i13_get_ids 2, 1, @id output, @num_ids output
SELECT @id as N'@base_id';
insert into dbo.TBL_LOCATIONS (objectid, X_Coord, Y_Coord, Loc_Name)
values (@id,296163,3961644, 'new test2');
EXEC dbo.i13_get_ids 2, 1, @id output, @num_ids output
SELECT @id as N'@base_id';
insert into dbo.TBL_LOCATIONS (objectid, X_Coord, Y_Coord, Loc_Name)
values (@id,296563,3961644, 'new test3');
Surely there has to be a more efficient way?
Here is the procedure that is retrieving the next ID:
GO
/****** Object: StoredProcedure [dbo].[i13_get_ids] ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[i13_get_ids]
@id_type integer,
@num_requested_ids integer,
@base_id integer OUTPUT,
@num_obtained_ids integer OUTPUT AS SET NOCOUNT ON
BEGIN
IF (@num_requested_ids < 0)
BEGIN
BEGIN TRAN id_tran
/* We are resetting the generator. */
/* Delete fragments and update the base value.*/
UPDATE ATBI.DBO.i13 WITH (tablockx, holdlock)
SET base_id = base_id + @num_requested_ids
WHERE num_ids = -1 AND id_type = @id_type
DELETE FROM ATBI.DBO.i13 WHERE id_type = @id_type and num_ids <> -1
COMMIT TRAN id_tran /* releases holdlock table lock */
END
ELSE
BEGIN
IF (@num_requested_ids > 0)
BEGIN
DECLARE I_cursor CURSOR FOR
SELECT base_id, num_ids
FROM ATBI.DBO.i13 WITH (tablockx, holdlock)
WHERE id_type = @id_type
ORDER BY num_ids DESC /* ensures that fragments come first */
FOR UPDATE /* to get a lock */
END
ELSE
BEGIN
/* only interested in base id */
DECLARE I_cursor CURSOR FOR
SELECT base_id, num_ids
FROM ATBI.DBO.i13 WITH (tablockx, holdlock)
WHERE id_type = @id_type AND num_ids = -1
FOR UPDATE /* to get a lock */
END
BEGIN TRAN id_tran
OPEN I_cursor
FETCH NEXT FROM I_cursor INTO @base_id, @num_obtained_ids
IF (@num_requested_ids = 0)
BEGIN
/* Just getting current value */
SET @num_obtained_ids = 0
END
ELSE
BEGIN
IF (@num_obtained_ids = -1)
BEGIN
/* user got the amount they wanted */
SET @num_obtained_ids = @num_requested_ids
/* update the last id and base id */
UPDATE ATBI.DBO.i13
SET base_id = base_id + @num_obtained_ids,
last_id = @base_id
WHERE CURRENT OF I_cursor
END
ELSE
BEGIN
/* user got a fragment */
IF (@num_requested_ids = 1) AND (@num_obtained_ids > 1)
BEGIN
/* they want one and exactly one id */
SET @num_obtained_ids = 1
UPDATE ATBI.DBO.i13 SET base_id = base_id + 1,
num_ids = num_ids - 1 WHERE CURRENT OF I_cursor
END
ELSE
BEGIN
/* Return the whole fragment, delete the the row */
DELETE FROM ATBI.DBO.i13 WHERE CURRENT OF I_cursor
END
END
END
CLOSE I_cursor
COMMIT TRAN id_tran /* releases holdlock table lock */
DEALLOCATE I_cursor
END
END
GO
based on answer below, have implemented:
DECLARE @id INT, @num_ids INT;
EXEC [dbo].[i13_get_ids] 2, 10, @id OUTPUT, @num_ids OUTPUT;
INSERT dbo.TBL_Locations(objectid, X_Coord, Y_Coord, Loc_name)
SELECT N, 296563, 3961644, 'new test'
FROM dbo.oidNumbers WHERE n >= @id AND n < @id + @num_ids;
First, you should have a numbers table (some background that I've written here and here). I'm picking 100,000 rows but you may need more or less depending on how much you're going to use it.
Now, you should be able to simply say (I'm just picking 10 arbitrarily):
When you believe it's returning the right set, uncomment the
--INSERT
line. Just don't go too crazy because you are exhausting their supply of ids.Sorry, the stuff below this line was written with the assumption you were using unique identifiers, as your question originally stated, not integers. Leaving it in case it is useful to others.
If you create a stored procedure that can generate multiple GUIDs, imagine this procedure that returns a set of GUIDs:
Then you could do: