Redefinir semente de identidade após excluir registros no SQL Server

682

Eu inseri registros em uma tabela de banco de dados do SQL Server. A tabela tinha uma chave primária definida e a semente de identidade de incremento automático é definida como "Sim". Isso é feito principalmente porque no SQL Azure, cada tabela precisa ter uma chave primária e uma identidade definidas.

Mas como eu tenho que excluir alguns registros da tabela, a semente de identidade para essas tabelas será alterada e a coluna de índice (gerada automaticamente com um incremento de 1) será alterada.

Como posso redefinir a coluna de identidade depois de excluir os registros para que a coluna tenha sequência em ordem numérica crescente?

A coluna de identidade não é usada como chave estrangeira em nenhum lugar do banco de dados.

xorpower
fonte
4
"no SQL Azure" - "cada tabela precisa ter uma chave primária" - true - "e Identity Defined" - false. Identidade e chave primária são conceitos ortogonais. Uma coluna de identidade não precisa ser a PK de uma tabela. Uma chave primária não precisa ser uma coluna de identidade.
Damien_The_Unbeliever
ESTÁ BEM. Meu conceito pode estar errado. Mas agora eu defini a estrutura da tabela com PK e Identity Seed. Se eu tiver que apagar algumas linhas, como eu poderia redefinir Identidade semente em uma ordem crescente numérica correta
xorpower
29
Eu sempre argumentaria que, se você se importa com os valores numéricos reais gerados em uma coluna de identidade, os está usando incorretamente. Tudo o que você deve se preocupar com uma coluna de identidade é que ela gera automaticamente valores exclusivos (yay!) E que você pode armazenar esses valores em uma coluna numérica (esse bit é relevante apenas para declarar colunas para manter esses valores). Você não deve mostrá-los a ninguém, por isso não importa quais valores eles assumem.
21714 Damien_The_Unbeliever
você pode usar a verificação dbcc identificar como mencionado, mas observe que a chave primária não é obrigatória para o sql db v12
Satya_MSFT 5/16

Respostas:

1099

O DBCC CHECKIDENTcomando de gerenciamento é usado para redefinir o contador de identidade. A sintaxe do comando é:

DBCC CHECKIDENT (table_name [, { NORESEED | { RESEED [, new_reseed_value ]}}])
[ WITH NO_INFOMSGS ]

Exemplo:

DBCC CHECKIDENT ('[TestTable]', RESEED, 0);
GO

Não era suportado em versões anteriores do Banco de Dados SQL do Azure, mas agora é suportado.


Observe que o new_reseed_valueargumento varia de acordo com as versões do SQL Server, de acordo com a documentação :

Se houver linhas na tabela, a próxima linha será inserida com o valor new_reseed_value . Na versão SQL Server 2008 R2 e versões anteriores, a próxima linha inserida usa new_reseed_value + o valor atual de incremento.

No entanto, acho essas informações enganosas (na verdade, simplesmente erradas) porque o comportamento observado indica que pelo menos o SQL Server 2012 ainda usa new_reseed_value + a lógica atual do valor de incremento. A Microsoft até contradiz o que Example Cencontrou na mesma página:

C. Forçando o valor atual da identidade para um novo valor

O exemplo a seguir força o valor atual da identidade na coluna AddressTypeID na tabela AddressType a um valor 10. Como a tabela possui linhas existentes, a próxima linha inserida usará 11 como o valor, ou seja, o novo valor de incremento atual definido para o valor da coluna mais 1.

USE AdventureWorks2012;  
GO  
DBCC CHECKIDENT ('Person.AddressType', RESEED, 10);  
GO

Ainda assim, isso deixa uma opção para um comportamento diferente nas versões mais recentes do SQL Server. Acho que a única maneira de ter certeza, até a Microsoft esclarecer as coisas em sua própria documentação, é fazer testes reais antes do uso.

Petr Abdulin
fonte
23
A sintaxe seria ... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
Biki
2
Parece que DBCC CHECKIDENThá suporte para o próximo lançamento (V12 / Sterling): azure.microsoft.com/en-us/documentation/articles/… Embora, para essa situação em particular, eu ainda recomendo TRUNCATE TABLE :)
Solomon Rutzky
1
Não funcionou para mim até o "GO" estar em outra linha.
Mrówa #
1
A sintaxe está sendo sinalizada por causa da palavra-chave GO na mesma linha, não sei por quê. Você pode movê-lo para baixo de uma linha. Copiei e colei esta linha 50 vezes e agora tenho que voltar e corrigi-la.
AnotherDeveloper 4/16
4
Funcionou perfeitamente para mim. É de salientar que quando re-semeando uma mesa, se você quiser propagar novamente para que seu primeiro registro é ID 1, em seguida, o comando nova propagação deve propagar novamente a 0, de modo que o próximo registro é ID 1.
Mike Upjohn
215
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

Onde 0 é o identityvalor inicial

anil shah
fonte
15
Se a tabela estiver vazia, como se você acabou de ligar TRUNCATE, o novo valor inicial deve ser o valor para o próximo uso (ou seja, 1 e não 0). Se a tabela não estiver vazia, ela usará o new_reseed_value + 1. MSDN
kjbartel 28/01
2
@kjbartel, Anil e outros: não é tão simples como apenas "se a mesa estiver vazia". Faltava a documentação o caso de quando a tabela está vazia devido a DELETE, não TRUNCATE, nesse caso também new_reseed+value + 1. Eu escrevi um post sobre isso, mostrando o comportamento real através de alguns testes e atualizei o documento real (agora que podemos, devido ao fato de estar no GitHub): Como o DBCC CHECKIDENT realmente funciona ao redefinir a semente de identidade (RESEED)? .
Solomon Rutzky 10/04/19
87

Deve-se observar que, se todos os dados estiverem sendo removidos da tabela por meio da DELETE(ou seja, sem WHEREcláusula), desde que a) as permissões permitam eb) não houver FKs fazendo referência à tabela (que parece ser caso aqui), o uso TRUNCATE TABLEseria preferido, pois é mais eficiente DELETE e redefine a IDENTITYsemente ao mesmo tempo. Os seguintes detalhes são obtidos da página MSDN para TRUNCATE TABLE :

Comparado com a instrução DELETE, TRUNCATE TABLE possui as seguintes vantagens:

  • Menos espaço no log de transações é usado.

    A instrução DELETE remove as linhas uma por vez e registra uma entrada no log de transações para cada linha excluída. TRUNCATE TABLE remove os dados desalocando as páginas de dados usadas para armazenar os dados da tabela e registra apenas as desalocações da página no log de transações.

  • Normalmente, menos bloqueios são usados.

    Quando a instrução DELETE é executada usando um bloqueio de linha, cada linha da tabela é bloqueada para exclusão. TRUNCATE TABLE sempre bloqueia a tabela (incluindo um bloqueio de esquema (SCH-M)) e a página, mas não cada linha.

  • Sem exceção, zero páginas são deixadas na tabela.

    Depois que uma instrução DELETE é executada, a tabela ainda pode conter páginas vazias. Por exemplo, páginas vazias em um heap não podem ser desalocadas sem pelo menos um bloqueio de tabela exclusivo (LCK_M_X). Se a operação de exclusão não usar um bloqueio de tabela, a tabela (heap) conterá muitas páginas vazias. Para índices, a operação de exclusão pode deixar páginas vazias para trás, embora essas páginas sejam desalocadas rapidamente por um processo de limpeza em segundo plano.

Se a tabela contiver uma coluna de identidade, o contador dessa coluna será redefinido para o valor inicial definido para a coluna. Se nenhuma semente foi definida, o valor padrão 1 é usado. Para manter o contador de identidade, use DELETE.

Então, o seguinte:

DELETE FROM [MyTable];
DBCC CHECKIDENT ('[MyTable]', RESEED, 0);

Torna-se apenas:

TRUNCATE TABLE [MyTable];

Consulte a TRUNCATE TABLEdocumentação (vinculada acima) para obter informações adicionais sobre restrições, etc.

Solomon Rutzky
fonte
8
Embora seja mais eficiente nas circunstâncias corretas, isso nem sempre é uma opção. Truncar não será executado em uma tabela que tenha um FK definido. Mesmo quando não há registros dependentes, o truncado falhará se a restrição existir. Além disso, o truncado requer permissões ALTER, onde a exclusão precisa apenas DELETE.
precisa saber é o seguinte
3
@Rozwel True, mas eu já havia qualificado minha resposta afirmando que as permissões apropriadas precisam estar em vigor. Além disso, a pergunta afirma especificamente que não há FKs. No entanto, por uma questão de clareza, atualizei para especificar a restrição "no FK". Obrigado por apontar isso.
Solomon Rutzky
1
O único problema é que qualquer FK bloqueará o truncamento. É possível (embora incomum) ter um FK em relação a uma restrição exclusiva que não faz parte do PK ou das colunas de identidade.
Rozwel 9/01/2015
1
@Rozwel Novamente verdade, mas parece razoável supor da pergunta que não há restrições exclusivas, pois a PK existe apenas devido ao entendimento do OP (correto ou não) de que é exigido pelo Banco de Dados SQL do Azure. Independentemente disso, sou a favor de reduzir a ambiguidade, por isso atualizei novamente. Obrigado.
Solomon Rutzky
Não é tão incomum ter uma chave estrangeira em uma tabela, e a presença de QUALQUER chave estrangeira proíbe TRUNCATE TABLE. Acabei de descobrir isso da maneira mais difícil hoje, quando tentei executar TRUNCATE TABLE em uma tabela que possui uma chave estrangeira aplicada a outras duas colunas da tabela e um índice exclusivo na tabela estrangeira.
David A. Gray
83

Embora a maioria das respostas esteja sugerindo RESEED para 0, muitas vezes precisamos apenas reenviar para o próximo ID disponível

declare @max int
select @max=max([Id])from [TestTable]
if @max IS NULL   //check when max is returned as null
  SET @max = 0
DBCC CHECKIDENT ('[TestTable]', RESEED,@max)

Isso irá verificar a tabela e redefinir para o próximo ID.

Atal Kishore
fonte
2
Esta é a única resposta que funciona 100% do tempo
Reversed Engineer
3
Um pouco mais curto:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
Guillermo Prandi
61

Eu tentei @anil shahsresponder e redefinir a identidade. Mas quando uma nova linha foi inserida, ela recebeu o identity = 2. Então, em vez disso, mudei a sintaxe para:

DELETE FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED, 0)
GO

Em seguida, a primeira linha receberá a identidade = 1.

Mikael Engver
fonte
16

Embora a maioria das respostas estão sugerindo RESEEDque 0, e enquanto alguns vêem isso como uma falha de TRUNCATEDtabelas, a Microsoft tem uma solução que exclui oID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Isso verificará a tabela e redefinirá para a próxima ID . Isso está disponível desde o MS SQL 2005 até o momento.

https://msdn.microsoft.com/en-us/library/ms176057.aspx

RealSollyM
fonte
1
Infelizmente isso não é verdade. Apenas verifiquei isso no servidor MS SQL 2014.
alehro 8/09/2015
1
Na verdade, isso é verdade para o SQL 2014. Acabei de testá-lo e funcionou para mim.
Daniel Dyson
2
Isso funciona de maneira inconsistente para mim no SQL 2012. Às vezes, ele usa o próximo disponível como eu esperava, às vezes parece ficar preso em um valor antigo da tabela. A especificação da semente já funciona.
Dan Field
Não funciona para mim no SQL 2016 - apenas deixa a semente da identidade como está. Pode ter funcionado corretamente para mim uma vez, mas também pode ter sido um problema no meu dedo. Não pode fazê-lo funcionar novamente
Engenheiro invertida
A mensagem indica sucesso, Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.mas após novas inserções, ela ainda está usando a semente incorreta.
Denziloe
7

emitir 2 comando pode fazer o truque

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

o primeiro redefine a identidade para zero e o próximo define o próximo valor disponível - jacob

jacob
fonte
2
O DBCC CHECKIDENT ('[TestTable]', RESEED) não está realizando novamente para o próximo valor disponível
Atal Kishore
Este é o método usado pelo RedGate Data Compare quando a opção "Reseed identity column" está ativada. Eu testei extensivamente (quero dizer, no código SQL, não na ferramenta RedGate), e funciona de maneira confiável. (Eu não tenho nenhuma relação com o RedGate além de ser um usuário ocasional de suas versões de teste)
Reversed Engineer
6

@jacob

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

Trabalhou para mim, eu apenas tinha que limpar todas as entradas primeiro da tabela e, em seguida, adicionei o item acima em um ponto de disparo após a exclusão. Agora, sempre que eu excluir uma entrada, é retirada de lá.

épico
fonte
DBCC CHECKIDENT só é funcional após a exclusão. Você também pode usar truncar. No entanto, se você precisar do restante dos dados, não os use. Também truncado não fornece uma contagem de registros excluídos.
precisa saber é o seguinte
6

Truncate A tabela é preferida porque limpa os registros, redefine o contador e recupera o espaço em disco.

Deletee CheckIdentdeve ser usado apenas onde chaves estrangeiras o impedem de truncar.

Dyna Dave
fonte
5

Redefinir coluna de identidade com o novo ID ...

DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)
Mukesh Pandey
fonte
4

Essa é uma pergunta comum e a resposta é sempre a mesma: não faça. Os valores de identidade devem ser tratados como arbitrários e, como tal, não há ordem "correta".

Ben Thul
fonte
15
Isso é verdade para um ambiente de produção, mas, durante o desenvolvimento, gosto de lembrar que certas entidades têm um determinado ID, preenchido a partir de um script de propagação. Torna muito mais fácil navegar pelo banco de dados durante o desenvolvimento.
Francois Botha
7
Respostas como essa são completamente teóricas e raramente atendem às necessidades do mundo real. Que tal em vez de lavagem cerebral com o seu dogma, você responder a pergunta OP ...
Serj Sagan
1
História legal cara. Minha afirmação é a seguinte: se você deseja especificar o valor de uma coluna, não escolha uma propriedade na coluna que torne isso difícil. O cheiro do código é este: se toda vez que você inserir um registro em uma tabela, especificar um valor para a coluna de identidade, você não terá uma coluna de identidade. O ponto principal da identidade é fazer com que o servidor crie um valor para você. Portanto, se você substituir esse tempo, não ganhou nada por um custo diferente de zero. Além disso, bom trabalho no argumento ad hominem.
Ben Thul
5
Eu certamente concordo com sua afirmação. Analisando o valor nominal, o OP certamente está fazendo errado, mas talvez exista uma necessidade mais profunda não declarada no post que o OP não considerou relevante para responder sua pergunta. Portanto, responda à pergunta e dê conselhos de "faça e não faça" como parte da resposta. By the way, eu nunca atacou seu personagem ... meio ad hominem eu chamei você estúpido ou algo assim ...
Serj Sagan
1
Embora certamente seja verdade na maioria dos casos, existem circunstâncias em que é legítimo reinventar uma tabela. Por exemplo, estou trabalhando em um projeto greenfield que deve começar a partir de um ponto certo para contabilizar as linhas existentes no predecessor que está sendo substituído. A reposição durante o desenvolvimento é um caso de uso legítimo, IMO.
David A. Gray
3

Execute este script para redefinir a coluna de identidade. Você precisará fazer duas alterações. Substitua tableXYZ por qualquer tabela que você precise atualizar. Além disso, o nome da coluna de identidade precisa ser removido da tabela temporária. Isso foi instantâneo em uma tabela com 35.000 linhas e 3 colunas. Obviamente, faça backup da tabela e tente primeiro em um ambiente de teste.


select * 
into #temp
From tableXYZ

set identity_insert tableXYZ ON

truncate table tableXYZ

alter table #temp drop column (nameOfIdentityColumn)

set identity_insert tableXYZ OFF

insert into tableXYZ
select * from #temp
Matthew Baic
fonte
3
Isso não está totalmente correto: o SET IDENTITY_INSERT está no lugar errado. Ele não gira em torno do TRUNCATE, gira em torno do INSERT INTO (daí a identidade_ INSERT ). Além disso, isso deve ser usado apenas quando os dados precisam ser mantidos; caso contrário, é muito ineficiente se comparado à execução da única instrução TRUNCATE.
Solomon Rutzky
1
DBCC CHECKIDENT (<TableName>, reseed, 0)

Isso definirá o valor atual da identidade como 0.

Ao inserir o próximo valor, o valor da identidade é incrementado para 1.

Bimzee
fonte
1

Use este procedimento armazenado:

IF (object_id('[dbo].[pResetIdentityField]') IS NULL)
  BEGIN
    EXEC('CREATE PROCEDURE [dbo].[pResetIdentityField] AS SELECT 1 FROM DUMMY');
  END
GO

SET  ANSI_NULLS ON
GO
SET  QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[pResetIdentityField]
  @pSchemaName NVARCHAR(1000)
, @pTableName NVARCHAR(1000) AS
DECLARE @max   INT;
DECLARE @fullTableName   NVARCHAR(2000) = @pSchemaName + '.' + @pTableName;

DECLARE @identityColumn   NVARCHAR(1000);

SELECT @identityColumn = c.[name]
FROM sys.tables t
     INNER JOIN sys.schemas s ON t.[schema_id] = s.[schema_id]
     INNER JOIN sys.columns c ON c.[object_id] = t.[object_id]
WHERE     c.is_identity = 1
      AND t.name = @pTableName
      AND s.[name] = @pSchemaName

IF @identityColumn IS NULL
  BEGIN
    RAISERROR(
      'One of the following is true: 1. the table you specified doesn''t have an identity field, 2. you specified an invalid schema, 3. you specified an invalid table'
    , 16
    , 1);
    RETURN;
  END;

DECLARE @sqlString   NVARCHAR(MAX) = N'SELECT @maxOut = max(' + @identityColumn + ') FROM ' + @fullTableName;

EXECUTE sp_executesql @stmt = @sqlString, @params = N'@maxOut int OUTPUT', @maxOut = @max OUTPUT

IF @max IS NULL
  SET @max = 0

print(@max)

DBCC CHECKIDENT (@fullTableName, RESEED, @max)
go

--exec pResetIdentityField 'dbo', 'Table'

Apenas revisitando minha resposta. Me deparei com um comportamento estranho no sql server 2008 r2 que você deve estar ciente.

drop table test01

create table test01 (Id int identity(1,1), descr nvarchar(10))

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

delete from test01

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

A primeira seleção produz 0, Item 1 .

O segundo produz 1, Item 1. Se você executar a redefinição logo após a criação da tabela, o próximo valor será 0. Honestamente, não estou surpreso que a Microsoft não consiga acertar essas coisas. Eu o descobri porque tenho um arquivo de script que preenche as tabelas de referência que às vezes executo após a recriação de tabelas e outras quando as tabelas já foram criadas.

costa
fonte
1

Eu uso o seguinte script para fazer isso. Existe apenas um cenário no qual ele produzirá um "erro", ou seja, se você excluiu todas as linhas da tabela e IDENT_CURRENTestá atualmente definido como 1, ou seja, havia apenas uma linha na tabela para começar.

DECLARE @maxID int = (SELECT MAX(ID) FROM dbo.Tbl)
;

IF @maxID IS NULL
    IF (SELECT IDENT_CURRENT('dbo.Tbl')) > 1
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 0)
    ELSE
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 1)
    ;
ELSE
    DBCC CHECKIDENT ('dbo.Tbl', RESEED, @maxID)
;
Chris Mack
fonte
0

Para obter linhas DELETE completas e redefinir a contagem de IDENTITY, eu uso isso (SQL Server 2008 R2)

USE mydb

-- ##################################################################################################################
-- DANGEROUS!!!! USE WITH CARE
-- ##################################################################################################################

DECLARE
  db_cursor CURSOR FOR
    SELECT TABLE_NAME
      FROM INFORMATION_SCHEMA.TABLES
     WHERE TABLE_TYPE = 'BASE TABLE'
       AND TABLE_CATALOG = 'mydb'

DECLARE @tblname VARCHAR(50)
SET @tblname = ''

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @tblname

WHILE @@FETCH_STATUS = 0
BEGIN
  IF CHARINDEX('mycommonwordforalltablesIwanttodothisto', @tblname) > 0
    BEGIN
      EXEC('DELETE FROM ' + @tblname)
      DBCC CHECKIDENT (@tblname, RESEED, 0)
    END

  FETCH NEXT FROM db_cursor INTO @tblname
END

CLOSE db_cursor
DEALLOCATE db_cursor
GO
Fandango68
fonte
0

A reposição de 0 não é muito prática, a menos que você esteja limpando a tabela como um todo.

de outro modo, a resposta dada por Anthony Raymond é perfeita. Obtenha o máximo da coluna de identidade primeiro e depois propague-o com o máx.

Ali Sufyan
fonte
0

Eu tenho tentado fazer isso para um grande número de tabelas durante o desenvolvimento, e isso funciona como um encanto.

DBCC CHECKIDENT('www.newsType', RESEED, 1);
DBCC CHECKIDENT('www.newsType', RESEED);

Portanto, você primeiro força a configuração para 1 e, em seguida, o índice mais alto das linhas presentes na tabela. Descanso rápido e fácil do idex.

KimvdLinde
fonte
-2

É sempre melhor usar TRUNCATE quando possível, em vez de excluir todos os registros, pois ele também não usa espaço de log.

No caso de precisarmos excluir e redefinir a semente, lembre-se sempre de que, se a tabela nunca foi preenchida e você o usou DBCC CHECKIDENT('tablenem',RESEED,0) , o primeiro registro terá identidade = 0, conforme indicado na documentação do msdn

No seu caso, apenas recrie o índice e não se preocupe em perder a série de identidade, pois esse é um cenário comum.

Abdul Hannan Ijaz
fonte
3
Parece-me que a ideia é apenas excluir alguns registros.
Drumbeg
6
Isso está errado - não é sempre melhor usar truncado e, de fato, só é melhor em alguns cenários muito limitados e específicos. O céu proíbe que alguém siga seus conselhos e depois revire.
Thronk
1
@Thronk Por que você está insinuando que TRUNCATEisso impediria o ROLLBACKcomportamento esperado? ROLLBACK ainda retrocede. Mesmo se o banco de dados estiver definido como BULK_LOGGED.
Solomon Rutzky 5/17/17
2
TRUNCATE é uma operação DDL e não está registrado no arquivo de log. A menos que faça parte da transação (não mencionado em nenhum lugar da pergunta ou nesta resposta). Sempre que alguém diz que algo SEMPRE é verdade, é uma aposta bastante segura que eles estão errados.
Thronk
Essa é a única resposta que observa que há uma diferença no comportamento RESEED, dependendo se a sequência foi usada anteriormente ou não. Uma nova propagação do mesmo valor entre várias tabelas vazias , onde algumas tabelas foram preenchidas anteriormente, resultará em diferentes valores iniciais para o primeiro registro inserido em cada tabela.
simon coleman
-4

Primeiro: Especificação de identidade apenas: "Não" >> Salvar projeto de execução de banco de dados

Depois disso: Especificação de identidade apenas: "SIM" >> Salvar projeto de execução de banco de dados

Seu ID do banco de dados, PK Iniciar a partir de 1 >>

Pratik Patel
fonte