SQL Server DBCC CHECKIDENT: Bulk Identity Column Analysis
DBCC CHECKIDENT with NORESEED reports the current identity value of a table next to the largest value actually stored in the identity column, without changing either. It works on one table per call, so checking a whole database means writing one command per table. The script below generates those commands from the catalog views, limited to tables that really have an identity column, and adds a read-only inventory of seed, increment and last value.
Purpose and Overview
sys.identity_columns is the catalog view that lists every identity column in a database. It returns every column of sys.columns for those columns and adds seed_value, increment_value, last_value and is_not_for_replication. Joined to sys.tables and sys.schemas, it gives the exact set of tables DBCC CHECKIDENT can be run against, which is the part a hand-written list always gets wrong over time.
The command itself requires that the table contain an identity column, so a generator that simply emits one statement per user table produces errors for every table without one. Filtering through sys.identity_columns removes those. The generator also schema-qualifies and delimits each name, because the command needs delimiters on two-part names such as [Person].[AddressType], and a name that needs delimiters would otherwise fail.
The usual reason to run this is a mismatch between the identity counter and the data: rows deleted from the table, a manual RESEED, or inserts that failed or were rolled back, since a consumed identity value is never handed out again. NORESEED shows the two numbers side by side. If they differ, the identity value can be reset to avoid errors or gaps in the sequence. This script only reports; deciding whether to reseed stays a manual step.
Code Breakdown
The script has three parts: a single-table check, the command generator, and the identity inventory.
1-- 1. Check one table without changing anything
2DBCC CHECKIDENT ('dbo.Jobs', NORESEED);
3
4-- 2. Generate a NORESEED check for every user table that has an identity column
5SELECT
6 'DBCC CHECKIDENT (''' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ''', NORESEED);' AS check_command
7FROM sys.tables AS t
8INNER JOIN sys.schemas AS s
9 ON s.schema_id = t.schema_id
10WHERE EXISTS (
11 SELECT 1
12 FROM sys.identity_columns AS ic
13 WHERE ic.object_id = t.object_id
14)
15ORDER BY s.name, t.name;
16
17-- 3. Identity inventory: type, seed, increment, last value and current identity
18SELECT
19 s.name AS schema_name,
20 t.name AS table_name,
21 ic.name AS column_name,
22 TYPE_NAME(ic.user_type_id) AS data_type,
23 ic.seed_value,
24 ic.increment_value,
25 ic.last_value,
26 IDENT_CURRENT(QUOTENAME(s.name) + '.' + QUOTENAME(t.name)) AS current_identity
27FROM sys.identity_columns AS ic
28INNER JOIN sys.tables AS t
29 ON t.object_id = ic.object_id
30INNER JOIN sys.schemas AS s
31 ON s.schema_id = t.schema_id
32ORDER BY s.name, t.name;
Checking a single table
DBCC CHECKIDENT (table_name, NORESEED) specifies that the current identity value should not be changed. It returns the current identity value and the current maximum value of the identity column. The message has this shape:
1Checking identity information: current identity value '<current identity value>', current column value '<current column value>'.
2DBCC execution completed. If DBCC printed error messages, contact your system administrator.
If the two numbers are not the same, the identity value can be reset to avoid errors or gaps in the sequence. The caller must own the schema that contains the table, or be a member of sysadmin, db_owner or db_ddladmin. WITH NO_INFOMSGS suppresses the informational messages.
The same command without NORESEED is not a read-only check. DBCC CHECKIDENT (table_name) and DBCC CHECKIDENT (table_name, RESEED) reset the identity value to the maximum value in the column when the current identity value is lower than that maximum. DBCC CHECKIDENT (table_name, RESEED, new_reseed_value) sets the current identity value to whatever is given. That is why the generator hard-codes NORESEED.
Generating the commands
The generator reads sys.tables, which returns a row for each user table, so no type = 'U' filter is needed. sys.schemas supplies the schema name through schema_id. The EXISTS test against sys.identity_columns keeps only tables with an identity column. QUOTENAME wraps each name in brackets and doubles any closing bracket inside the name, so unusual names still produce a valid delimited identifier.
The output is one DBCC CHECKIDENT statement per row, ending in a semicolon. Copy the result column into a new query window and run it. Each statement returns its own message, so on a database with many identity tables the messages tab is the result.
An older form of this script reads sysobjects with type = 'U'. sysobjects is a SQL Server 2000 system table kept as a view for backward compatibility, and it is marked for removal in a future version, with a recommendation to avoid it in new work. It also returns every user table, identity or not, which is a further reason the generator above works from the current views.
The identity inventory query
sys.identity_columns supplies seed_value, increment_value and last_value. All three are sql_variant values of the same data type as the column itself. last_value is the last value generated for the column. TYPE_NAME(ic.user_type_id) turns the inherited user_type_id into a type name, which matters for the next check: the type decides how much room is left.
IDENT_CURRENT returns the last identity value generated for the table in any session and any scope. When the table has never contained rows, or has been truncated, it returns the seed value. Failed statements and rolled-back transactions can change the current identity of a table and leave gaps, because the identity value is never rolled back. For that reason IDENT_CURRENT should not be used to predict the next value: other sessions may insert in between, so the next value can differ from IDENT_CURRENT plus the increment.
Key Benefits and Use Cases
- Complete coverage - the command list comes from the catalog, so a table added last week is included without anyone editing a list.
- No false errors - only tables with an identity column are listed, so the run does not fail on tables the command cannot check.
- Safe to run -
NORESEEDleaves the identity value unchanged, so the generated batch can be run against production. - Mismatch detection - the two numbers in the
NORESEEDmessage show whether the identity value and the column maximum still agree after deletes, reseeds or failed inserts. - Overflow watch - the data type, seed, increment and last value in the inventory show how much of an
intorsmallintrange has been consumed. - Before-and-after comparison - saving the inventory before and after a large delete or a manual reseed shows exactly what moved.
Performance Considerations
- Permissions for the DBCC calls: the caller must own the schema that contains the table, or be a member of
sysadmin,db_ownerordb_ddladmin, so a generated batch run under a weaker login fails table by table. - Output volume: every generated statement returns its own result message, so the output grows with the number of identity tables.
- Permissions limit what is seen: visibility of metadata in catalog views is limited to securables that the user owns or has been granted some permission on, and
IDENT_CURRENTreturns NULL when the caller has no permission to view the object. A low-privilege login can produce a short or empty list without any error. - Run it per database: the views are database-scoped, so the script has to be run in each database being checked, or wrapped in a loop over databases.
- Pair with a reseed only after reading the result:
RESEEDwith a value lower than the column maximum raises error 2627 on later inserts when a primary key or unique constraint exists on the column, and produces duplicate identity values when none does.
Practical Tips
- Save the output of the first run as a baseline. A later run that shows a different gap between the two numbers is the signal worth investigating.
- Check the data type column in the inventory first. An identity column on
intstops at 2,147,483,647, andsmallintat 32,767.bigintreaches 9,223,372,036,854,775,807.tinyintstops at 255. - Do not treat gaps as corruption. The identity property does not guarantee consecutive values: values can be lost after a server restart or failure because SQL Server might cache identity values, and a rolled-back insert consumes a value that is never generated again.
- Do not rely on identity for uniqueness. Uniqueness has to be enforced by a
PRIMARY KEYorUNIQUEconstraint or a unique index. - When the check shows a current identity value larger than the column maximum, the command does not reset it on its own. Reseed explicitly to the column maximum, or reseed to a low value and then run
RESEEDwith no value to correct it. - After deleting every row from a table,
RESEEDwith the new starting value is the way to reset the identity value. - To check one table in a non-default schema, pass the delimited two-part name in quotes, for example
'Person.AddressType'or'[Person].[AddressType]'.
Conclusion
DBCC CHECKIDENT with NORESEED answers one question per table: does the identity counter agree with the data? Driving it from sys.tables, sys.schemas and sys.identity_columns makes that question cover the whole database without a hand-kept list, and the inventory query adds the type and last value that show how close a column is to its limit. The script reports only; any reseed is a separate, deliberate command.
References
- DBCC CHECKIDENT (Transact-SQL) — Syntax, NORESEED and RESEED behaviour, result messages and required permissions.
- sys.identity_columns (Transact-SQL) — Columns for seed, increment and last value, and its inheritance from sys.columns.
- sys.tables (Transact-SQL) — One row per user table.
- sys.schemas (Transact-SQL) — Schema names and ids used to qualify table names.
- sys.columns (Transact-SQL) — Source of the inherited object_id, name and user_type_id columns.
- sys.sysobjects (Transact-SQL) — Compatibility view status and the object type codes.
- IDENT_CURRENT (Transact-SQL) — Scope of the value, NULL and seed behaviour, and the caution about predicting the next value.
- IDENTITY property (Transact-SQL) — What the identity property does and does not guarantee, including gaps and reuse.
- int, bigint, smallint, and tinyint (Transact-SQL) — Value ranges for the integer types.
- QUOTENAME (Transact-SQL) — Delimiting identifiers and escaping closing brackets.
- TYPE_NAME (Transact-SQL) — Converting a type ID to a type name.
Posts in this series
- SQL Server Scripts and Commands: DBA Reference All Versions
- SQL Server DBCC CHECKIDENT: Bulk Identity Column Analysis
- SQL Server Error Log Search Script: xp_readerrorlog
- SQL Server Search Stored Procedure and View Text
- SQL Server Agent Job Failure History Report
- SQL Server Kill Sessions: Filtered SPID Management
- SQL Server MAXDOP Recommendation Script
- SQL Server Max Server Memory Calculator Script
- SQL Server Object Dependencies Report: Find All References
- SQL Server DBCC CHECKIDENT: Check and Reseed Identity
- List All Tables Across All Databases in SQL Server
- Configure Database Mail and Send a Test Message
- Audit Server Configuration Drift with sys.configurations
- Check AG Replica Health with sys.dm_hadr_availability_replica_states
- Audit Per-Database MAXDOP, CE and Sniffing Config Overrides