Сбросить семя идентификации после удаления записей в SQL Server

682

Я вставил записи в таблицу базы данных SQL Server. Для таблицы был определен первичный ключ, и для начального идентификатора с автоприращением установлено значение «Да». Это делается главным образом потому, что в SQL Azure для каждой таблицы должны быть определены первичный ключ и идентификатор.

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

Как я могу сбросить столбец идентификаторов после удаления записей, чтобы столбец имел последовательность в порядке возрастания номеров?

Столбец идентификации не используется в качестве внешнего ключа в базе данных.

xorpower
источник
4
«в SQL Azure» - «каждая таблица должна иметь первичный ключ» - true - и «Identity Defined» - false. Идентичность и первичный ключ являются ортогональными понятиями. Столбец идентификации не обязательно должен быть PK таблицы. Первичный ключ не обязательно должен быть столбцом идентификации.
Damien_The_Unbeliever
ХОРОШО. Моя концепция может быть неправильной. Но теперь я определил структуру таблицы с помощью PK и Identity Seed. Если мне нужно удалить некоторые строки, как я могу сбросить Identity Seed в правильном порядке возрастания
чисел
29
Я бы всегда утверждал, что если вам небезразличны фактические числовые значения, сгенерированные в столбце идентификаторов, вы их неправильно используете. Все, что вас должно волновать в столбце идентификаторов, - это то, что он автоматически генерирует уникальные значения (ууу!) И что вы можете сохранять эти значения в числовом столбце (этот бит важен только для объявления столбцов для хранения этих значений). Вы не должны показывать их кому-либо, поэтому не должно иметь значения, какие ценности они принимают.
Damien_The_Unbeliever
Вы можете использовать проверку идентификатора dbcc, как и другие упомянутые, но обратите внимание, что первичный ключ не является обязательным для sql db v12
Satya_MSFT

Ответы:

1100

Команда DBCC CHECKIDENTуправления используется для сброса счетчика идентификаторов. Синтаксис команды:

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

Пример:

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

Это не поддерживалось в предыдущих версиях базы данных SQL Azure, но теперь поддерживается.


Обратите внимание, что new_reseed_valueаргумент варьируется в зависимости от версии SQL Server в соответствии с документацией :

Если в таблице присутствуют строки, следующая строка вставляется со значением new_reseed_value . В версии SQL Server 2008 R2 и более ранних версиях следующая вставленная строка использует new_reseed_value + текущее значение приращения.

Однако я нахожу эту информацию вводящей в заблуждение (на самом деле просто неверной), поскольку наблюдаемое поведение указывает, что по крайней мере SQL Server 2012 все еще использует new_reseed_value + логику текущего значения приращения. Microsoft даже противоречит собственному, Example Cнайденному на той же странице:

C. Принуждение текущего значения идентичности к новому значению

В следующем примере значение текущего идентификатора в столбце AddressTypeID в таблице AddressType устанавливается равным 10. Поскольку в таблице есть существующие строки, в следующей вставленной строке в качестве значения будет использоваться 11, то есть новое текущее значение приращения, определенное для значение столбца плюс 1.

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

Тем не менее, все это оставляет возможность для другого поведения в новых версиях SQL Server. Я предполагаю, что единственный способ быть уверенным, пока Microsoft не прояснит ситуацию в своей собственной документации, это сделать реальные тесты перед использованием.

Петр Абдулин
источник
24
Синтаксис будет ... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
Biki
2
Похоже, что DBCC CHECKIDENTподдерживается в следующем выпуске (V12 / Sterling): azure.microsoft.com/en-us/documentation/articles/… Хотя, для этой конкретной ситуации, я все равно рекомендую TRUNCATE TABLE :)
Solomon Rutzky
1
Это не сработало для меня, пока "GO" не был в другой строке.
mrówa
1
Синтаксис помечается из-за ключевого слова GO в той же строке, я не знаю почему. Можете ли вы переместить его вниз по линии. Я скопировал и вставил эту строку 50 раз, и теперь я должен вернуться и исправить ее.
AnotherDeveloper
4
Работал отлично для меня. Стоит отметить, что при повторном заполнении таблицы, если вы хотите выполнить повторное заполнение, чтобы ваша первая запись была идентификатором 1, тогда команда
Майк Апджон
216
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

Где 0 - identityначальное значение

Анил Шах
источник
15
Если таблица пуста, например, если вы только что позвонили TRUNCATE, то новое начальное значение должно быть значением для следующего использования (т. Е. 1, а не 0). Если таблица не пуста, она будет использовать new_reseed_value + 1. MSDN
kjbartel
2
@kjbartel, Anil и другие: это не так просто, как просто «если таблица пуста». В документации отсутствовал случай, когда таблица пуста из-за DELETE, нет TRUNCATE, в этом случае это тоже так new_reseed+value + 1. Я написал сообщение об этом, показывающее реальное поведение с помощью некоторых тестов, и обновил фактический документ (теперь, когда мы можем это сделать из-за того, что он находится на GitHub): Как действительно работает DBCC CHECKIDENT при сбросе семени идентификации (RESEED)? ,
Соломон Руцкий,
87

Следует отметить, что ЕСЛИ все данные удаляются из таблицы через DELETE(то есть без WHEREпредложения), то, если это разрешено разрешениями a), и b) отсутствуют FK, ссылающиеся на таблицу (которая, по-видимому, является случай здесь), использование TRUNCATE TABLEбыло бы предпочтительным, так как это делает более эффективным DELETE и сбрасывает IDENTITYсемя в то же время. Следующие подробности взяты со страницы MSDN для TRUNCATE TABLE :

По сравнению с оператором DELETE TRUNCATE TABLE имеет следующие преимущества:

  • Меньше места в журнале транзакций.

    Оператор DELETE удаляет строки по одной за раз и записывает запись в журнал транзакций для каждой удаленной строки. TRUNCATE TABLE удаляет данные, освобождая страницы данных, используемые для хранения данных таблицы, и записывает только освобождения страниц в журнал транзакций.

  • Обычно используется меньше замков.

    Когда оператор DELETE выполняется с использованием блокировки строк, каждая строка в таблице блокируется для удаления. TRUNCATE TABLE всегда блокирует таблицу (включая блокировку схемы (SCH-M)) и страницу, но не каждую строку.

  • Все без исключения нулевые страницы остаются в таблице.

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

Если таблица содержит столбец идентификаторов, счетчик для этого столбца сбрасывается на начальное значение, определенное для столбца. Если начальное число не было определено, используется значение по умолчанию 1. Чтобы сохранить счетчик идентификаторов, используйте вместо этого DELETE.

Итак, следующее:

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

Становится просто:

TRUNCATE TABLE [MyTable];

Пожалуйста, смотрите TRUNCATE TABLEдокументацию (ссылка выше) для получения дополнительной информации об ограничениях и т. Д.

Соломон Руцкий
источник
8
Хотя это более эффективно при правильных обстоятельствах, это не всегда вариант. Усечение не будет выполняться для таблицы, для которой определен FK. Даже если нет зависимых записей, усечение завершится ошибкой, если ограничение существует. Также для усечения требуются разрешения ALTER, когда для удаления требуется только УДАЛЕНИЕ.
Розвель
3
@Rozwel Верно, но я уже уточнил свой ответ, заявив, что надлежащие разрешения должны быть на месте. Также в вопросе конкретно говорится, что нет ФК. Однако, для ясности, я обновил, чтобы указать ограничение "нет FK". Спасибо что подметил это.
Соломон Руцкий
1
единственное утешение в том, что любой FK будет блокировать усечение. Возможно (хотя и необычно) иметь FK против уникального ограничения, которое не является частью PK или столбцов идентификации.
Розвель
1
@Rozwel Снова верно, но кажется разумным предположить из вопроса, что не существует уникальных ограничений, учитывая, что PK существует только из-за понимания OP (правильного или нет), что это требуется для базы данных SQL Azure. Независимо от того, я все для уменьшения двусмысленности, поэтому я обновил снова. Спасибо.
Соломон Руцкий
Не так уж необычно иметь внешний ключ на столе, а наличие ЛЮБОГО внешнего ключа запрещает TRUNCATE TABLE. Я только что обнаружил это сложным способом ранее сегодня, когда пытался запустить TRUNCATE TABLE для таблицы с внешним ключом, который принудительно применяется к двум другим столбцам в таблице и уникальному индексу во внешней таблице.
Дэвид А. Грей
83

Хотя большинство ответов предлагают RESEED до 0, но во многих случаях нам нужно просто повторно заполнить до следующего доступного Id

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)

Это проверит таблицу и вернется к следующему идентификатору.

Атал Кишоре
источник
2
Это единственный ответ, который работает 100% времени
Обратный инженер
3
Немного короче:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
Гильермо
61

Я попытался @anil shahsответить, и это сбросило личность. Но когда был вставлен новый ряд, он получил identity = 2. Поэтому вместо этого я изменил синтаксис:

DELETE FROM [TestTable]

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

Тогда первый ряд получит тождество = 1.

Микаэль Энгвер
источник
sqlserver 2008r2 dbcc для checkident
stackuser83
16

Несмотря на то, что большинство ответов предполагают , RESEEDчтобы 0, и в то время как некоторые видят это как недостаток для TRUNCATEDтаблиц, Microsoft имеет решение, исключающиеID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Это проверит таблицу и вернется к следующему ID. Это было доступно с MS SQL 2005 до настоящего времени.

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

RealSollyM
источник
1
К сожалению, это не так. Только что проверил это для сервера MS SQL 2014.
Alehro
1
На самом деле, это верно для SQL 2014. Я только что проверил его, и он работал для меня.
Даниэль Дайсон
2
Это работает противоречиво для меня в SQL 2012. Иногда он использует следующий доступный, как я и ожидал, иногда он застревает на старом значении из таблицы. Указание семени всегда работает.
Дэн Филд
У меня не работает на SQL 2016 - он просто оставляет семя идентификации как есть. Возможно, это сработало правильно для меня один раз, но это также могло быть моей проблемой с пальцем. Не могу заставить его работать снова
Обратный инженер
Сообщение указывает на успех, Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.но при новых вставках оно все еще использует неправильное начальное число.
Дензилое
7

выдача 2 команды может сделать свое дело

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

первый сбрасывает тождество на ноль, а следующий устанавливает его на следующее доступное значение - jacob

Джекоб
источник
2
DBCC CHECKIDENT ('[TestTable]', RESEED) не перезаполняется до следующего доступного значения
Atal Kishore
Этот метод используется в RedGate Data Compare, когда включена опция «Повторная идентификация столбцов». Я тщательно его протестировал (я имею в виду код SQL, а не инструмент RedGate), и он работает надежно. (Я не имею никакого отношения к RedGate, кроме как случайный пользователь их пробных версий)
Reversed Engineer
6

@jacob

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

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

эпический
источник
DBCC CHECKIDENT работает только после удаления. Вы могли бы также использовать усечение. Однако, если вам нужны остальные данные, не используйте их. Также усечение не дает количество записей удаленных записей.
user763539
6

Truncate Таблица предпочтительна, потому что она очищает записи, сбрасывает счетчик и освобождает место на диске.

Deleteи CheckIdentдолжен использоваться только там, где внешние ключи не позволяют вам усекать.

Дина Дейв
источник
5

Сброс идентификатора столбца с новым идентификатором ...

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

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)
Мукеш Пандей
источник
4

Это общий вопрос, и ответ всегда один и тот же: не делайте этого. Значения идентичности должны рассматриваться как произвольные, и, как таковой, нет «правильного» порядка.

Бен Тул
источник
15
Это верно для производственной среды, но при разработке мне нравится помнить, что у определенных объектов есть определенный Id, который заполняется из сценария заполнения. Это значительно облегчает навигацию по базе данных в процессе разработки.
Франсуа Бота
7
Подобные ответы являются абсолютно теоретическими и редко соответствуют реальным потребностям мира. Как насчет того, чтобы вместо промывания мозгов людям своей догмой вы отвечали на вопрос ОП ...
Серж Саган
1
Классная история, братан. Я утверждаю следующее: если вы хотите указать значение для столбца, не выбирайте свойство для столбца, которое затрудняет выполнение этого. Запах кода такой: если каждый раз, когда вы вставляете запись в таблицу, вы указываете значение для столбца идентификаторов, у вас нет столбца идентификаторов. Весь смысл в том, чтобы сервер создавал ценность для вас. Поэтому, если вы переопределите это время, вы ничего не получите за ненулевую цену. Кроме того, хорошая работа над аргументом ad hominem.
Бен Тул
5
Я, конечно, согласен с вашим утверждением. Что касается номинальной стоимости, ОП, безусловно, делает это неправильно, но, возможно, в сообщении не указывается, что ОП не считает уместным получение ответа на свой вопрос. Следовательно, ответьте на вопрос и дайте совет «что делать и чего не делать» как часть ответа. Кстати, я никогда не атаковал вашего персонажа ... ad hominem означает, что я назвал вас глупым или что-то в этом роде ...
Серж Саган,
1
Несмотря на то, что в большинстве случаев это действительно так, в некоторых случаях существуют обстоятельства, при которых правомерно пересматривать таблицу. Например, я работаю над новым проектом, который должен начинаться с определенной точки, чтобы учесть существующие строки в предшественнике, который он заменяет. Пересев во время разработки является законным вариантом использования, ИМО.
Дэвид А. Грей
3

Запустите этот скрипт, чтобы сбросить столбец идентификаторов. Вам нужно будет сделать два изменения. Замените tableXYZ на любую таблицу, которую нужно обновить. Кроме того, имя столбца идентификаторов необходимо удалить из временной таблицы. Это было мгновенно для таблицы с 35 000 строк и 3 столбцами. Очевидно, сделайте резервную копию таблицы и сначала попробуйте это в тестовой среде.


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
Мэтью Байк
источник
3
Это не совсем правильно: SET IDENTITY_INSERT находится не в том месте. Он не обходит TRUNCATE, он обходит INSERT INTO (следовательно, identity_ INSERT ). Кроме того, это следует использовать только тогда, когда необходимо хранить данные, иначе это очень неэффективно по сравнению с простым выполнением одного оператора TRUNCATE.
Соломон Руцки
1
DBCC CHECKIDENT (<TableName>, reseed, 0)

Это установит текущее значение идентичности в 0.

При вставке следующего значения значение идентификатора увеличивается до 1.

Bimzee
источник
1

Используйте эту хранимую процедуру:

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'

Просто вернусь к моему ответу. Я столкнулся со странным поведением в SQL Server 2008 R2, о котором вы должны знать.

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

Первый выбор производит 0, Item 1.

Второй производит 1, Item 1. Если вы выполните сброс сразу после того, как таблица создана, следующее значение равно 0. Честно говоря, я не удивлен, что Microsoft не может сделать это правильно. Я обнаружил это, потому что у меня есть файл сценария, который заполняет справочные таблицы, которые я иногда запускаю после повторного создания таблиц, а иногда, когда таблицы уже созданы.

ребро
источник
1

Я использую следующий скрипт для этого. Есть только один сценарий, в котором он выдаст «ошибку», если вы удалили все строки из таблицы, и IDENT_CURRENTв настоящее время установлено значение 1, то есть в таблице была только одна строка для начала.

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)
;
Крис Мак
источник
0

Для полного удаления строк и сброса счетчика IDENTITY я использую это (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
источник
0

Повторное заполнение до 0 не очень практично, если вы не чистите стол в целом.

В противном случае ответ Энтони Рэймонда идеален. Сначала получите максимальный столбец идентификаторов, затем начните с макс.

Али Суфьян
источник
0

Я пытался сделать это для большого количества таблиц во время разработки, и это работает как очарование.

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

Итак, сначала вы устанавливаете значение 1, а затем устанавливаете на самый высокий индекс из строк, представленных в таблице. Быстрый и легкий отдых от idex.

KimvdLinde
источник
-2

Всегда лучше использовать TRUNCATE, когда это возможно, вместо удаления всех записей, поскольку он также не использует пространство журнала.

В случае, если нам нужно удалить и нам нужно сбросить начальное число, всегда помните, что если таблица никогда не заполнялась и вы использовали ее, DBCC CHECKIDENT('tablenem',RESEED,0) то первая запись получит identity = 0, как указано в документации по msdn.

В вашем случае только перестройте индекс и не беспокойтесь о потере серии идентификаторов, так как это распространенный сценарий.

Абдул Ханнан Иджаз
источник
3
Похоже, идея состоит в том, чтобы удалить только некоторые записи.
Drumbeg
6
Это просто неправильно - <i> ВСЕГДА </ i> не лучше использовать усечение и, на самом деле, лучше только в некоторых, очень ограниченных и специфических сценариях. Боже упаси кто-нибудь последовать твоему совету и потом нужно откатиться.
Thronk
1
@ Thronk Почему вы намекаете на то, что TRUNCATEэто помешает вам ROLLBACKвести себя, как ожидалось? ROLLBACK по-прежнему откатывается. Даже если БД установлена ​​в BULK_LOGGED.
Соломон Руцкий,
2
TRUNCATE является операцией DDL и не регистрируется в файле журнала. Если это не является частью транзакции (не упоминается нигде в вопросе или в этом ответе). Всякий раз, когда кто-то говорит, что что-то ВСЕГДА верно, вполне вероятно, что они ошибаются.
Тронк
Это единственный ответ, который отмечает, что есть различие в поведении RESEED в зависимости от того, использовалась ли ранее последовательность или нет. Повторное заполнение одного и того же значения в нескольких пустых таблицах, где некоторые таблицы были ранее заполнены, приведет к различным начальным значениям для первой записи, вставленной в каждую таблицу.
Саймон Коулман
-4

Во-первых: просто укажите спецификацию: «Нет» >> Сохранить базу данных Выполнить проект

После этого: Идентификация идентификации просто: "ДА" >> Сохранить базу данных Выполнить проект

Ваш идентификатор базы данных, ПК Начните с 1 >>

Пратик Патель
источник