DBCC CHECKIDENT Setzt die Identität auf 0

75

Ich verwende diesen Code, um die Identität einer Tabelle zurückzusetzen:

DBCC CHECKIDENT('TableName', RESEED, 0)

Dies funktioniert die meiste Zeit einwandfrei. Beim ersten Einfügen füge ich 1 in die ID-Spalte ein. Wenn ich jedoch die Datenbank lösche und neu erstelle (mithilfe von Skripten, die ich geschrieben habe) und dann DBCC CHECKIDENT aufrufe, hat das erste eingefügte Element die ID 0.

Irgendwelche Ideen?

BEARBEITEN: Nach Recherchen stellte ich fest, dass ich die Dokumentation nicht richtig gelesen habe - "Der aktuelle Identitätswert wird auf new_reseed_value gesetzt. Wenn seit der Erstellung keine Zeilen in die Tabelle eingefügt wurden, wird die erste nach dem Ausführen von DBCC CHECKIDENT eingefügte Zeile eingefügt Verwenden Sie new_reseed_value als Identität. Andernfalls wird in der nächsten eingefügten Zeile new_reseed_value + 1 verwendet. "

user47437
quelle

Antworten:

26

Wie Sie in Ihrer Frage betont haben, handelt es sich um ein dokumentiertes Verhalten . Ich finde es trotzdem seltsam. Ich verwende, um die Testdatenbank neu zu füllen, und obwohl ich mich nicht auf die Werte von Identitätsfeldern verlasse, war es etwas ärgerlich, unterschiedliche Werte zu haben, wenn die Datenbank zum ersten Mal von Grund auf neu gefüllt wurde und nachdem alle Daten entfernt und erneut ausgefüllt wurden.

Eine mögliche Lösung besteht darin , die Tabelle mit " Abschneiden" zu bereinigen, anstatt sie zu löschen. Dann müssen Sie jedoch alle Einschränkungen löschen und anschließend neu erstellen

Auf diese Weise verhält es sich immer wie eine neu erstellte Tabelle und es ist nicht erforderlich, DBCC CHECKIDENT aufzurufen. Der erste Identitätswert ist der in der Tabellendefinition angegebene und er ist der gleiche, unabhängig davon, ob Sie die Daten zum ersten Mal oder zum N-ten einfügen

kristof
quelle
2
Dieser Link scheint tot zu sein. Die aktuelle Dokumentation msdn.microsoft.com/en-us/library/ms176057.aspx besagt, dass dies nur für Versionen vor 2012 erwartet wird, aber das scheint nicht zu stimmen (Testen auf Version 2012, Kompatibilität auf 2012 eingestellt, Problem weiterhin).
jmoreno
67

Sie haben Recht mit dem, was Sie in der Bearbeitung Ihrer Frage schreiben.

Nach dem Ausführen DBCC CHECKIDENT('TableName', RESEED, 0):
- Neu erstellte Tabellen beginnen mit Identität 0
- Vorhandene Tabellen werden mit Identität 1 fortgesetzt

Die Lösung ist im Skript unten, es ist eine Art Poor-Mans-Truncate :)

-- Remove all records from the Table
DELETE FROM TableName

-- Use sys.identity_columns to see if there was a last known identity value
-- for the Table. If there was one, the Table is not new and needs a reset
IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = 'TableName' AND last_value IS NOT NULL) 
    DBCC CHECKIDENT (TableName, RESEED, 0);
Zyphrax
quelle
2
Sehr gut , aber wenn Sie mit mehreren Schemata zu tun können Sie diese ein wenig optimieren wollen passen object_idanstelle der Verwendung OBJECT_NAME()der Tabelle Namen zu sehen: IF EXISTS (SELECT * FROM sys.identity_columns WHERE object_id = OBJECT_ID('Schema.TableName') AND last_value IS NOT NULL)
Nick
5

Ändern Sie die Anweisung in

  DBCC CHECKIDENT('TableName', RESEED, 1)

Dies beginnt bei 2 (oder 1, wenn Sie eine Tabelle neu erstellen), ist jedoch niemals 0.

dmajkic
quelle
3

Ich habe dies als Experiment durchgeführt, um den Wert auf 0 zurückzusetzen, da meine erste Identitätsspalte 0 sein soll und es funktioniert.

dbcc CHECKIDENT(MOVIE,RESEED,0)
dbcc CHECKIDENT(MOVIE,RESEED,-1)
DBCC CHECKIDENT(MOVIE,NORESEED)
Ashish Kumar Jaryal
quelle
3

Es erscheint lächerlich, dass Sie eine Identitätsspalte nicht mit einem einzigen Befehl festlegen / zurücksetzen können, um beide Fälle abzudecken, in denen in die Tabelle Datensätze eingefügt wurden oder nicht. Ich konnte das Verhalten, das ich erlebte, nicht verstehen, bis ich auf SO!

Meine Lösung (hässlich, aber funktioniert) besteht darin, die sys.identity_columns.last_valueTabelle explizit zu überprüfen (was Ihnen sagt, ob in die Tabelle Datensätze eingefügt wurden oder nicht) und jeweils den entsprechenden DBCC CHECKIDENTBefehl aufzurufen . Es ist wie folgt:

DECLARE @last_value INT = CONVERT(INT, (SELECT last_value FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = 'MyTable'));
IF @last_value IS NULL
    BEGIN
        -- Table newly created and no rows inserted yet; start the IDs off from 1
        DBCC CHECKIDENT ('MyTable', RESEED, 1);
    END
ELSE
    BEGIN
        -- Table has rows; ensure the IDs continue from the last ID used
        DECLARE @lastValUsed INT = (SELECT ISNULL(MAX(ID),0) FROM MyTable);
        DBCC CHECKIDENT ('MyTable', RESEED, @lastValUsed);
    END
Matthew Wise
quelle
1

Ich habe dies in SQL verwendet, um IDENTITY auf einen bestimmten Wert zu setzen: -

DECLARE @ID int = 42;
DECLARE @TABLENAME  varchar(50) = 'tablename'

DECLARE @SQL nvarchar(1000) = 'IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = '''+@TABLENAME+''' AND last_value IS NOT NULL)
    BEGIN
        DBCC CHECKIDENT('+@TABLENAME+', RESEED,' + CONVERT(VARCHAR(10),@ID-1)+');
    END
    ELSE
    BEGIN
        DBCC CHECKIDENT('+@TABLENAME+', RESEED,' + CONVERT(VARCHAR(10),@ID)+');
    END';
EXEC (@SQL);

Und dies in C #, um einen bestimmten Wert festzulegen: -

SetIdentity(context, "tablename", 42);
.
.
private static void SetIdentity(DbContext context, string table,int id)
{
    string str = "IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = '" + table
        + "' AND last_value IS NOT NULL)\nBEGIN\n";
    str += "DBCC CHECKIDENT('" + table + "', RESEED," + (id - 1).ToString() + ");\n";
    str += "END\nELSE\nBEGIN\n";
    str += "DBCC CHECKIDENT('" + table + "', RESEED," + (id).ToString() + ");\n";
    str += "END\n";
    context.Database.ExecuteSqlCommand(str);
}

Dies baut auf den obigen Antworten auf und stellt immer sicher, dass der nächste Wert 42 ist (in diesem Fall).

user2284063
quelle
1

Aus Zyphrax 'Antwort entlehnt ...

USE DatabaseName

DECLARE @ReseedBit BIT = 
    COALESCE((SELECT SUM(CONVERT(BIGINT, ic.last_value))
                FROM sys.identity_columns ic
                INNER JOIN sys.tables t ON ic.object_id = t.object_id), 0)
DECLARE @Reseed INT = 
CASE 
    WHEN @ReseedBit = 0 THEN 1 
    WHEN @ReseedBit = 1 THEN 0 
END

DBCC CHECKIDENT ('dbo.table_name', RESEED, @Reseed);

Vorsichtsmaßnahmen: Dies ist für die Verwendung in Referenzdatenpopulationssituationen vorgesehen, in denen eine Datenbank mit Aufzählungstypdefinitionstabellen initialisiert wird, wobei die ID-Werte in diesen Tabellen immer bei 1 beginnen müssen. Beim ersten Erstellen der Datenbank (z. B. während der SSDT-) DB-Veröffentlichung) @Reseed muss 0 sein, aber wenn Sie die Daten zurücksetzen, dh die Daten entfernen und erneut einfügen, muss @Reseed 1 sein. Dieser Code ist also für die Verwendung in einer gespeicherten Prozedur zum Zurücksetzen der DB-Daten vorgesehen manuell aufgerufen werden, wird aber auch über das Post-Deployment-Skript im SSDT-DB-Projekt aufgerufen. Auf diese Weise werden die Referenzdateneinfügungen nur an einer Stelle definiert, dürfen jedoch nicht nur nach der Bereitstellung während der Veröffentlichung verwendet werden. Sie können auch später verwendet werden (zur Unterstützung von Entwicklern und automatisierten Tests usw.).

jjee
quelle
Es gibt eine einfachere Möglichkeit, dasselbe zu tun. Verzeihen Sie meine Formatierung. DECLARE @Reseed INT = (SELECT TOP 1 CASE WHEN CONVERT (BIGINT, ic.last_value)> 0 THEN 0 ELSE 1 END as reseed FROM sys.identity_columns ic INNER JOIN sys.tables t ON ic.object_id = t.object_id und t. object_id = OBJECT_ID (@tableName))
Nikhil Doomra
0

Mach das einfach:

IF EXISTS (SELECT * FROM tablename)
BEGIN
    DELETE from  tablename
    DBCC checkident ('tablename', reseed, 0)
END
Vishalcode
quelle
-2
USE AdventureWorks2012;  
GO  
DBCC CHECKIDENT ('Person.AddressType', RESEED, 0);  
GO 



AdventureWorks2012=Your databasename
Person.AddressType=Your tablename
user7208444
quelle
Dies macht nichts anderes als OP bereits. Wo ist die Lösung?
Gert Arnold