Подтвердить что ты не робот

Как получить следующее значение идентификатора из SQL Server

Мне нужно получить следующее значение идентичности от SQL Server.

Я использую этот код:

SELECT IDENT_CURRENT('table_name') + 1

Это правильно, но когда table_name пуст (а следующее значение - "1" ), возвращается "2", но результат равен "1"

4b9b3361

Ответ 1

Думаю, вам захочется найти альтернативный способ вычисления следующего доступного значения (например, установки автоматического увеличения числа столбцов).

В документации IDENT_CURRENT относительно пустых таблиц:

Когда значение IDENT_CURRENT равно NULL (поскольку таблица никогда не содержала строки или была усечена), функция IDENT_CURRENT возвращает начальное значение.

Это даже не кажется надежным, особенно если вы в конечном итоге создаете приложение, в котором одновременно написано несколько человек.

Будьте осторожны при использовании IDENT_CURRENT для прогнозирования следующего генерируемого значения идентификации. Фактическое сгенерированное значение может отличаться от IDENT_CURRENT плюс IDENT_INCR из-за вставок, выполняемых другими сеансами.

Ответ 2

Я склонен соглашаться с другими плакатами, что это неправильный способ сделать это, однако это может быть удобно для некоторых случаев. Несколько сообщений спрашивают, почему вообще это сделать, и позвольте мне привести вам пример, где это было удобно для меня, и как и почему.

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

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

Некоторые блоки содержат несколько тысяч транзакций. Некоторые транзакции для входов и/или выходов. Мне нужно, чтобы они были сохранены в БД из удобного вызова на С# без ущерба для целостности (все идентификаторы все связаны) и с достойной производительностью, которую я не могу получить, совершая ряд за строкой, когда есть около 10000 коммитов.

Вместо этого я абсолютно уверен, что я синхронизирую свой объект базы данных в С# во время операции (и мне также не нужно беспокоиться о других процессах, обращающихся к базе данных), поэтому я могу удобно сделать IDENT_CURRENT на всех 4 таблицах, вернуть значения из хранимой процедуры, заполнить почти 10000 строк в 4 List <DBTableRow> увеличивая идентификаторы и вызывая метод SqlBulkCopy.WriteToServer с параметром SqlBulkCopyOptions.KeepIdentity, а затем отправляет все это в 4 простых вызова, по одному для каждого табличного набора.

Производительность (на ноутбуке среднего класса 4-5 лет) составляла от 60 до 90 секунд до 2-3 секунд для действительно больших блоков, поэтому я был рад узнать о IDENT_CURRENT().

Решение может быть не изящным, может быть, не так по книге, но это удобно и просто. Есть и другие способы сделать это, я знаю, но это было просто прямо, и потребовалось несколько часов для реализации. Просто убедитесь, что у вас нет проблем concurrency.

Ответ 3

Если ваша таблица будет пустой, этот запрос будет работать отлично.

SELECT
  CASE
    WHEN (SELECT
        COUNT(1)
      FROM tablename) = 0 THEN 1
    ELSE IDENT_CURRENT('tablename') + 1
  END AS Current_Identity;

Ответ 4

Я знаю, что уже есть ответ, но мне действительно кажется, что все мои поисковые запросы по принципу "получить следующий идентификационный сервер sql" возникли с помощью хрупких решений (например, просто выбрав текущую идентификационную ценность и добавив 1) или "он не может быть надежно выполнен".

Есть несколько способов сделать это.

SQL Server >= 2012

CREATE SEQUENCE dbo.seq_FooId START WITH 1 INCREMENT BY 1
GO

CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        DEFAULT (NEXT VALUE FOR dbo.seq_FooId)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
DECLARE @next_id = NEXT VALUE FOR dbo.seq_FooId

В SQL Server 2012 появился объект SEQUENCE. В этом случае последовательность будет увеличиваться каждый раз, когда вызывается NEXT VALUE FOR, поэтому вам не нужно беспокоиться о concurrency.

SQL Server <= 2008

CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        IDENTITY (1, 1)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
BEGIN TRANSACTION
SELECT TOP 1 1 FROM dbo.Foos WITH (TABLOCKX, HOLDLOCK)
DECLARE @next_id int = IDENT_CURRENT('dbo.Foos') + IDENT_INCR('dbo.Foos');
DBCC CHECKIDENT('dbo.Foos', RESEED, @next_id)
COMMIT TRANSACTION

Вероятно, вы захотите инкапсулировать все это в хранимую процедуру, особенно потому, что оператор DBCC требует повышенного доступа, и вы, вероятно, не захотите, чтобы все имели такой доступ.

Не где-то рядом с таким элегантным, как NEXT VALUE FOR, но он должен быть надежным. Обратите внимание, что вы получите 2 для своего первого значения, если в таблице нет строк, но если вы намереваетесь всегда использовать этот метод для получения следующего идентификатора, вы можете записать идентификатор в 0 вместо 1IDENTITY (0, 1)), если вы мертвы, чтобы начать с 1.

Почему кто-то хочет это сделать?

Я не могу говорить за вопрос, но книга "Domain Driven Design" и "официальный" образец DDD использует этот метод (или по крайней мере, намекает на это) в качестве способа обеспечения того, чтобы эти объекты всегда имели действительный идентификатор. Если ваш объект имеет поддельный идентификатор (например, -1 или default(int) или null) до тех пор, пока в базу данных не будет INSERT ed, это потенциально может привести к проблемам с сохранением.

Ответ 5

SELECT isnull(IDENT_CURRENT('emp') + IDENT_INCR('emp'),1)