NEWID() для INT (BigInt, SmallInt) в T-SQL

По ходу работы столкнулся со следующей проблемой — необходимо было сгенерировать уникальный в пределах БД показатель типа INT. Вообще, для uniqueidentifier существет такая замечательная функция NEWID(), которая генерирует заветное значение, а вот для INT'а подобную функцию придется реализовывать самостоятельно.

Вообще, сделать подобно можно, сохранив уже сгенерированные IDшники в какую-нибудь таблицу. Но как реализовать генерацию нового значения?

Первой мыслью было использование транзакций, нахождение максимального числа с последующим его увеличением. Конечно, такой вариант тоже «прокатывает», но параноик-кун, затаившийся внутри меня, кричал о возможных таймаутах, ошибках в транзакции и прочих невкусностях.

И тут мне пришла довольно интересная идея, которую вкратце можно описать следующим алгоритмом:
1) создаем служебнную таблицу с 2мя полями — ID_int (типа int) и ID_guid (типа uniqueidentifier);
2) ставим автоинкремент и PK на ID_int;
3) создаем процедуру, которая вставляет в таблицу значение с новым гуидом;
4) выбираем по созданному гуиду интовский IDшник;
[5)] при необходимости удаляем строку (если место критическое).

Сказано — сделано: реализация.
1, 2) создание таблицы с автоинкрементом
USE [MyDB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[sys_NEWID_INT](
  [ID_INT] [int] IDENTITY(1,1) NOT NULL,
  [ID_GUID] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_sys_NEWID_INT] PRIMARY KEY CLUSTERED 
(
  [ID_INT] 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


2, 3, 4) создание процедуры

USE [MyDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[syssp_NEWID_INT](@ID_int int OUTPUT)
AS
BEGIN

  DECLARE    @ID_G  uniqueidentifier
  SET        @ID_G = NEWID()
  
  SET        @ID_int = NULL

  INSERT
  INTO    [sys_NEWID_INT]([ID_GUID])
  VALUES  (@ID_G)

  SELECT  @ID_int = ID_INT
  FROM    [sys_NEWID_INT]
  WHERE   ID_GUID = @ID_G
  
  DELETE
  FROM    [sys_NEWID_INT]
  WHERE   ID_INT = @ID_int

END


Плюсы и минусы подхода
плюс Никаких транзакций, реализуемых вручную.
плюс-минус В данном подходе используется процедура, а не функция, что доставляет некоторые неудобства (возможно, кому-то понравится).
минус Уязвимость к удалению служебной таблицы, но этот минус можно отнести к подходу в целом.

Готов дополнить статью вашими и своими идеями (в данный момент как раз занимаюсь этой проблемой).

Что дальше — варианты развития идеи
Данный код уже можно использовать, но возможно и его улучшение — например, если посмотреть Tuning Advisor'ом (я работаю с MS SQL Server) на текст хранимки, то можно получить рекомендации по расставлению индексов в данную таблицу (это при условии изменения ХП, если оставить «как есть», то индексы не нужны). Еще одним направлением по универсализации кода может быть проверка на наличие таблицы, которую мы создавали вручную, и её создание при необходимости. Ну и, конечно же, стоит присмотреться к отлову исключений.
П.С. Можно возвращать значения из ХП иначе, просто мне удобнее делать это через OUTPUT.
П.П.С. Для BigInt и SmallInt делать можно по аналогии с Int.


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.