A partir do SQL Server 2012 notei existe um bug na sequencia de autoincremrnto dos campos IDENTITY. Notei isso em 25/03/2012, ou seja, logo que lançaram o 2012. Postei no MSDN a questão e mesmo a confirmação do bug por parte de usuários experientes e certificados, pediram pra aguardar que muito possivelmente viria uma correção (ver post).
Mas veio o 2012 SP1 e o 2014 e o problema continua. Exemplo de um caso já no MSSQL 2014, onde o identity salta do 9 para o 1009:
Com o intuito de ajudar os que passam pelo mesmo problema e ainda aguardam uma solução por parte da Microsoft, vou postar uma solução paliativa (gambiarra) para evitar que o salto aconteça.
Basta executar a query abaixo e não haverá mais saltos do identity em todo o servidor (todos os bancos e tabelas, mesmo os criados depois).
Caso prefira troque o nome da Stored Procedure de sp_FixSeeds2012 para sp_FixSeeds[e o numero da sua versao], mas não interfere no funcionamento. Portanto aconselho não perder tempo com isso.
USE master; GO CREATE PROCEDURE sp_FixSeeds2012 AS BEGIN --foreach database DECLARE @DatabaseName varchar(255) DECLARE DatabasesCursor CURSOR READ_ONLY FOR SELECT name FROM sys.databases where name not in ('master','tempdb','model','msdb') and sys.databases.state_desc = 'online' OPEN DatabasesCursor FETCH NEXT FROM DatabasesCursor INTO @DatabaseName WHILE @@FETCH_STATUS = 0 BEGIN EXEC ('USE '+@DatabaseName + ' --foreach identity column DECLARE @tableName varchar(255) DECLARE @columnName varchar(255) DECLARE @schemaName varchar(255) DECLARE IdentityColumnCursor CURSOR READ_ONLY FOR select TABLE_NAME , COLUMN_NAME, TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, ''IsIdentity'') = 1 OPEN IdentityColumnCursor FETCH NEXT FROM IdentityColumnCursor INTO @tableName, @columnName, @schemaName WHILE @@FETCH_STATUS = 0 BEGIN print ''['+@DatabaseName+'].[''+@tableName+''].[''+@schemaName+''].[''+@columnName+'']'' EXEC (''declare @MAX int = 0 select @MAX = max(''+@columnName+'') from ['+@DatabaseName+'].[''+@schemaName+''].[''+@tableName+''] if (@MAX IS NULL) BEGIN SET @MAX = 0 END DBCC CHECKIDENT(['+@DatabaseName+'.''+@schemaName+''.''+@tableName+''],RESEED,@MAX)'') FETCH NEXT FROM IdentityColumnCursor INTO @tableName, @columnName, @schemaName END CLOSE IdentityColumnCursor DEALLOCATE IdentityColumnCursor') FETCH NEXT FROM DatabasesCursor INTO @DatabaseName END CLOSE DatabasesCursor DEALLOCATE DatabasesCursor END GO EXEC sp_configure 'show advanced options', 1 ; GO RECONFIGURE GO EXEC sp_configure 'scan for startup procs', 1 ; GO RECONFIGURE GO EXEC sp_procoption @ProcName = 'sp_FixSeeds2012' , @OptionName = 'startup' , @OptionValue = 'true' GO
Acho que assim fica bem melhor,
ResponderExcluirUSE [master]
GO
/****** Object: StoredProcedure [dbo].[sp_FixSeeds2012] Script Date: 20/12/2019 17:12:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_FixSeeds2012]
AS
BEGIN
DECLARE @txQUERY VARCHAR(MAX) = NULL
SET @txQUERY = CONCAT('USE [master];', CHAR(13), CHAR(10),'DECLARE @varTOTAL INT = 0;')
;WITH tmpDATABASE AS (
SELECT
[NAME] AS 'db_NAME'
FROM [sys].[databases]
WHERE name not in ('master','tempdb','model','msdb')
AND [sys].[databases].[state_desc] = 'online'
), tmpTABELA AS (
SELECT
a.TABLE_NAME AS 'tab_NAME',
a.COLUMN_NAME AS 'tab_COLUNA',
a.TABLE_SCHEMA AS 'tab_SCHEMA',
a.TABLE_CATALOG AS 'tab_DATABASE'
FROM [INFORMATION_SCHEMA].[COLUMNS] a
JOIN [SYSOBJECTS] b ON a.TABLE_NAME = b.NAME
WHERE (COLUMNPROPERTY(object_id(a.TABLE_NAME), a.COLUMN_NAME, 'IsIdentity') = 1)
AND b.TYPE = 'U'
)
SELECT
@txQUERY = CONCAT(
@txQUERY,
CHAR(13), CHAR(10), CHAR(13), CHAR(10),
'USE ', [db_NAME], ';',
CHAR(13), CHAR(10),
'SET @varTOTAL = ISNULL((SELECT MAX([',
[tab_COLUNA],
']) FROM [',
[tab_SCHEMA],
'].[',
[tab_NAME],
']), 0);',
CHAR(13), CHAR(10),
'PRINT ''++++++++++++++++++''',
CHAR(13), CHAR(10),
'PRINT ''[', [db_NAME],'].[', [tab_SCHEMA], '].[', [tab_NAME], ']''',
CHAR(13), CHAR(10),
'IF (@varTOTAL > 0) DBCC CHECKIDENT(''[',
[db_NAME],
'].[',
[tab_SCHEMA],
'].[',
[tab_NAME],
']'',''RESEED'', @varTOTAL);'
)
FROM tmpDATABASE a
JOIN tmpTABELA b ON a.db_NAME = b.tab_DATABASE
ORDER BY [db_NAME], [tab_NAME]
-- SELECT @txQUERY AS '@txQUERY'
-- PRINT '@txQUERY=' + @txQUERY
EXEC(@txQUERY)
--
RETURN
END
GO
Bom dia Amigo,
ResponderExcluirAchei interessante seu código, acredito que vai resolver meu problema aqui, muito obrigado!
Na verdade isso é um comportamento normal do SQL Server, em um transação que possua insert, um cache de identity por default reserva 1000 ids. Não me recordo agora com certeza, mas acho que isso é por uma questão de melhor performance ao realizar muitos inserts em uma mesma tabela em uma transação.
ResponderExcluirOcorre que se o serviço do MSSQL parar no meio de uma transação, que estava inserindo registros, sem passar pelo roolback ou commit, em uma eventual queda de energia, ou parada forçada do serviço, falha na rede, por exemplo, você vai notar esse "salto" de 1000 ids.
A nível de banco de dados, a partir da versão 2017 esse comportamento pode ser desativado com o comando:
ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFF;
Existem outras formas de fazer isso via Trace Flags na inicialização do servidor também, mas acredito que essa a nível de banco seja a mais interessante.