Мне нужно получить следующее значение идентичности от SQL Server
.
Я использую этот код:
SELECT IDENT_CURRENT('table_name') + 1
Это правильно, но когда table_name
пуст (а следующее значение - "1" ), возвращается "2", но результат равен "1"
Мне нужно получить следующее значение идентичности от SQL Server
.
Я использую этот код:
SELECT IDENT_CURRENT('table_name') + 1
Это правильно, но когда table_name
пуст (а следующее значение - "1" ), возвращается "2", но результат равен "1"
Думаю, вам захочется найти альтернативный способ вычисления следующего доступного значения (например, установки автоматического увеличения числа столбцов).
В документации IDENT_CURRENT относительно пустых таблиц:
Когда значение IDENT_CURRENT равно NULL (поскольку таблица никогда не содержала строки или была усечена), функция IDENT_CURRENT возвращает начальное значение.
Это даже не кажется надежным, особенно если вы в конечном итоге создаете приложение, в котором одновременно написано несколько человек.
Будьте осторожны при использовании IDENT_CURRENT для прогнозирования следующего генерируемого значения идентификации. Фактическое сгенерированное значение может отличаться от IDENT_CURRENT плюс IDENT_INCR из-за вставок, выполняемых другими сеансами.
Я склонен соглашаться с другими плакатами, что это неправильный способ сделать это, однако это может быть удобно для некоторых случаев. Несколько сообщений спрашивают, почему вообще это сделать, и позвольте мне привести вам пример, где это было удобно для меня, и как и почему.
Я реализую биткойн 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.
Если ваша таблица будет пустой, этот запрос будет работать отлично.
SELECT
CASE
WHEN (SELECT
COUNT(1)
FROM tablename) = 0 THEN 1
ELSE IDENT_CURRENT('tablename') + 1
END AS Current_Identity;
Я знаю, что уже есть ответ, но мне действительно кажется, что все мои поисковые запросы по принципу "получить следующий идентификационный сервер sql" возникли с помощью хрупких решений (например, просто выбрав текущую идентификационную ценность и добавив 1) или "он не может быть надежно выполнен".
Есть несколько способов сделать это.
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.
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
вместо 1
(с IDENTITY (0, 1)
), если вы мертвы, чтобы начать с 1.
Я не могу говорить за вопрос, но книга "Domain Driven Design" и "официальный" образец DDD использует этот метод (или по крайней мере, намекает на это) в качестве способа обеспечения того, чтобы эти объекты всегда имели действительный идентификатор. Если ваш объект имеет поддельный идентификатор (например, -1
или default(int)
или null
) до тех пор, пока в базу данных не будет INSERT
ed, это потенциально может привести к проблемам с сохранением.
SELECT isnull(IDENT_CURRENT('emp') + IDENT_INCR('emp'),1)