Скидання насіння ідентифікації після видалення записів на SQL Server


682

Я вставив записи в таблицю баз даних SQL Server. У таблиці було визначено первинний ключ, а насіння ідентифікатора автоматичного приросту встановлено на "Так". Це робиться насамперед тому, що в SQL Azure кожна таблиця повинна визначати первинний ключ та ідентичність.

Але оскільки мені доведеться видалити деякі записи з таблиці, насіння ідентичності для цих таблиць буде порушене, і стовпчик індексів (який автоматично генерується з збільшенням 1) буде порушений.

Як я можу скинути стовпець ідентичності після видалення записів, щоб стовпець мав послідовність у порядку числення?

Стовпець ідентичності не використовується як зовнішній ключ ніде в базі даних.


4
"in SQL Azure" - "кожна таблиця повинна мати первинний ключ" - true - "та визначений ідентичність" - false. Ідентичність та первинний ключ - це ортогональні поняття. Стовпець ідентичності не повинен бути ПК ПК таблиці. Первинний ключ не повинен бути стовпцем ідентичності.
Damien_The_Unbeliever

ДОБРЕ. Моя концепція може бути неправильною. Але тепер я визначив структуру таблиці за допомогою PK та Identity Seed. Якщо мені доведеться видалити декілька рядків, як я можу скинути Identity Seed у правильному числовому порядку, що
зростає

29
Я завжди заперечував би, що якщо ви дбаєте про фактичні числові значення, сформовані в стовпчику ідентичності, ви їх неправильно використовуєте. Все, що вам слід подбати про стовпчик ідентичності, - це те, що він автоматично генерує унікальні значення (так!) І що ви можете зберігати ці значення в числовому стовпчику (цей біт актуальний лише для оголошення оголошень стовпців, що містять ці значення). Ви не повинні показувати їх нікому, тому не має значення, які значення вони приймають.
Damien_The_Unbeliever

Ви можете використовувати dbcc check ідентифікувати як інше, але врахуйте, що для sql db v12 первинний ключ не є обов'язковим
Satya_MSFT

Відповіді:


1099

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

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

Приклад:

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

Він не підтримувався в попередніх версіях бази даних Azure SQL, але зараз підтримується.


Зауважте, що 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 не розбере речі у власній документації, - це зробити фактичні тести перед використанням.


23
Синтаксисом буде ... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
Biki

2
Схоже, DBCC CHECKIDENTце підтримується з моменту майбутнього випуску (V12 / Sterling): azure.microsoft.com/en-us/documentation/articles/… Хоча для цієї конкретної ситуації я все-таки рекомендую TRUNCATE TABLE :)
Соломон Руцький

1
Мені це не вийшло, поки "GO" не опинився в іншому рядку.
mrówa

1
Синтаксис позначається через ключове слово GO у тому ж рядку, я не знаю чому. Чи можете ви перемістити його вниз по лінії. Я скопіював і вставив цей рядок 50 разів, і тепер мені потрібно повернутися назад і виправити його.
розробник

4
Для мене прекрасно працювали. Варто зазначити, що при повторному засіданні таблиці, якщо ви хочете повторно заздалегідь перезапустити таблицю, щоб ваш перший запис був ID 1, тоді команда повторної повторної роботи повинна повторно встановити 0, щоб наступний запис був ідентифікатором 1.
Майк Апджон,

215
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пункту), тоді, поки а) дозволяють це дозволу, і б) немає 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, де для видалення потрібен лише DELETE.
Розвель

3
@Rozwel Правда, але я вже кваліфікував свою відповідь, вказуючи, що потрібні відповідні дозволи. Також у питанні конкретно сказано, що ФК немає. Однак для наочності я оновив, щоб вказати обмеження "без ФК". Дякуємо, що вказали на це.
Соломон Руцький

1
Єдиний каламбур - це те, що будь-який ФК блокує усічення. Можна (хоча і незвично) мати ФК проти унікального обмеження, яке не входить до ПК або стовпців ідентичності.
Розвель

1
@Rozwel Знову вірно, але здається, що з питання доцільно вважати, що немає унікальних обмежень, враховуючи, що ПК існує лише через розуміння ОП (правильне чи ні), що це вимагає база даних Azure SQL. Незважаючи на те, я все для зменшення двозначності, тому я знову оновився. Дякую.
Соломон Руцький

Не все так незвично, щоб на столі був зовнішній ключ, а наявність БУДЬ-якого зовнішнього ключа забороняє 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.



16

Незважаючи на те, що більшість відповідей припускають , RESEEDщоб 0, і в той час як деякі бачать це як недолік для TRUNCATEDтаблиць, Microsoft має рішення, що виключаютьID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Це дозволить перевірити таблицю і перейти до наступної ID. Це доступно з MS SQL 2005 по теперішній час.

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


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 Порівняти, коли включена опція "Створені стовпці ідентифікації". Я перевірив це широко (я маю на увазі в SQL-коді, а не в інструменті RedGate), і він працює надійно. (Я не маю жодного відношення до RedGate, окрім випадкових користувачів їхніх пробних версій)
Reversed Engineer

6

@jacob

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

Працював для мене, я просто повинен був очистити всі записи спочатку з таблиці, потім додав вищезазначене в тригерній точці після видалення. Тепер, коли я видаляю запис, зроблений звідти.


DBCC CHECKIDENT функціонує лише після видалення. Ви також можете використовувати усікання. Однак якщо вам потрібна решта даних, не використовуйте їх. Також усікання не дає кількість записів видалених.
користувач763539

6

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

Deleteі їх CheckIdentслід використовувати лише там, коли сторонні ключі не дозволяють вам виконувати обрізки.


5

Скинути стовпець посвідчення за допомогою нового id ...

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

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)

4

Це поширене питання, і відповідь завжди одна: не робіть цього. Значення ідентичності слід трактувати як довільне, і як таке немає "правильного" порядку.


15
Це справедливо для виробничого середовища, але, розвиваючись, я хотів би пам’ятати, що певні сутності мають певний ІД, який заповнюється за допомогою сценарію висіву. Це значно полегшує навігацію по базі даних під час розробки.
Франсуа Бота

7
Такі відповіді цілком теоретичні і рідко вони відповідають реальним світовим потребам. Як натомість замість того, щоб промивати мозок людьми з вашою догмою, ви відповідаєте на питання ОП ...
Serj Sagan

1
Крута історія брат. Моє твердження таке: якщо ви хочете вказати значення для стовпця, не вибирайте властивість стовпця, що ускладнює це. Запах коду такий: якщо кожен раз, коли ви вставляєте запис у таблицю, ви вказуєте значення для стовпця ідентичності, у вас немає стовпця ідентичності. Вся суть особистості полягає в тому, щоб сервер створив значення для вас. Тож якщо ви перекриєте це колись, ви нічого не отримаєте за ненульову вартість. Також хороша робота над аргументом ad hominem.
Бен Тул

5
Я, безумовно, згоден з вашим твердженням. Дивлячись на номінал, ОП, безумовно, робить це неправильно, але, можливо, є більш глибока потреба, не зазначена в посаді, що ОП не вважає за релевантне для отримання відповіді на його питання. Отже, дайте відповідь на питання і дайте поради "робити і не робити" як частину відповіді. До речі, я ніколи не нападав на твого персонажа ... ad hominem означає, що я назвав тебе дурним чи щось таке ...
Serj Sagan

1
Хоча, безумовно, правда в більшості випадків, існують обставини, за яких правомірним є повторне засідання таблиці. Наприклад, я працюю над проектом «зеленого поля», який повинен починатися з певного пункту для врахування існуючих рядків у попередника, який він замінює. Повторне дослідження під час розробки є законним випадком використання, IMO.
Девід А. Грей

3

Запустіть цей скрипт для скидання стовпця особи. Вам потрібно буде внести дві зміни. Замініть tableXYZ на будь-яку таблицю, яку потрібно оновити. Також ім'я стовпця особи повинно випадати з таблиці темп. Це було моментально на таблиці з 35000 рядками та 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, він іде навколо ВСТАВКИ INTO (звідси ідентифікація_ INSERT ). Крім того, це слід використовувати лише тоді, коли дані потрібно зберігати, інакше це дуже неефективно порівняно з просто запуском одного оператора TRUNCATE.
Соломон Руцький

1
DBCC CHECKIDENT (<TableName>, reseed, 0)

Це встановить поточне значення ідентичності на 0.

Після вставки наступного значення значення ідентифікації збільшують до 1.


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 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

Для повного видалення рядків і скидання кількості ідентичності я використовую це (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

0

Повторне використання до 0 не є дуже практичним, якщо ви не прибираєте стіл у цілому.

інший мудрий відповідь, яку дав Ентоні Реймонд, ідеальна. Спершу отримайте стовпець макс ідентичності, а потім надішліть його макс.


0

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

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

Отже, ви спочатку змушуєте його встановлювати на 1, потім встановлюєте його на найвищий індекс рядків, присутніх у таблиці. Швидкий та легкий відпочинок ідекс.


-2

Завжди краще використовувати TRUNCATE коли це можливо, замість видалення всіх записів, оскільки він також не використовує простір журналу.

У випадку, якщо нам потрібно видалити і потрібно скинути насіння, завжди пам’ятайте, що якщо таблиця ніколи не була заповнена, а ви використовували, DBCC CHECKIDENT('tablenem',RESEED,0) то перший запис отримає ідентичність = 0, як зазначено в документації msdn

У вашому випадку лише відбудуйте індекс і не переживайте про втрату ідентичності, оскільки це загальний сценарій.


3
Мені здається, що ідея - видалити лише деякі записи.
Драмбег

6
Це просто неправильно. Це не завжди! ВИНАГИ </i> краще використовувати усікання, а насправді це краще лише в деяких, дуже обмежених та конкретних сценаріях. Нехай Бог забороняє, щоб хтось дотримувався ваших порад, а потім потрібно відкидати.
Thronk

1
@Thronk Чому ти маєш на увазі, що TRUNCATEце заважатиме ROLLBACKповодитись так, як очікувалося? ROLLBACK все ще відхиляється. Навіть якщо встановлено DB BULK_LOGGED.
Соломон Руцький

2
TRUNCATE - це операція DDL, і вона не реєструється у файлі журналу. Якщо тільки це не є частиною транзакції (ніде не згадується у питанні чи у цій відповіді). Кожен раз, коли хтось каже, що ЗАВЖДИ правда, це цілком безпечна ставка, що вони помиляються.
Thronk

Це єдина відповідь, на яку зазначається, що різниця в поведінці RESEED залежить від того, використовувалася послідовність раніше чи ні. Повторна перевірка одного і того ж значення в кількох порожніх таблицях, де деякі таблиці були раніше заповнені, призведе до різних початкових значень для першого запису, вставленого у кожну таблицю.
симон-колег

-4

По-перше: Специфікація ідентичності Просто: "Ні" >> Зберегти виконувати проект бази даних

Після цього: Специфікація ідентичності Просто: "ТАК" >> Зберегти Виконати проект бази даних

Ваш ідентифікатор бази даних, ПК, починаючи з 1 >>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.