Query primary keys, unique constraints and indexes

Observed sign or problem

We need to understand how each row is identified and what structures exist to preserve uniqueness or support searches. Seeing just the name of an index is not enough: the order of its columns, the included columns, and their filters also matter.

What we need to distinguish

A primary key identifies every row uniquely, and a UNIQUE constraint prevents a combination of values from repeating. An index organizes values to make rows easier to access; it can support a constraint without being a model rule by itself.

A primary key and a unique constraint are supported by unique indexes, but not every unique index is a constraint. Additionally, a normal index may sort key columns and include others solely to cover a query.

Query

DECLARE @Tabla NVARCHAR(517) = N'dbo.Producto';
DECLARE @IdTabla INT = OBJECT_ID(@Tabla, N'U');

IF @IdTabla IS NULL
    THROW 50000, 'La tabla no existe o no es visible en esta base.', 1;

SELECT
    i.name AS Indice,
    CASE
        WHEN kc.type = N'PK' THEN N'LLAVE PRIMARIA'
        WHEN kc.type = N'UQ' THEN N'RESTRICCION UNICA'
        WHEN i.is_unique = 1 THEN N'INDICE UNICO'
        ELSE N'INDICE'
    END AS Clasificacion,
    i.type_desc AS TipoIndice,
    i.is_unique AS EsUnico,
    i.has_filter AS TieneFiltro,
    i.filter_definition AS Filtro,
    c.name AS Columna,
    ic.key_ordinal AS OrdenLlave,
    ic.is_descending_key AS EsDescendente,
    ic.is_included_column AS EsIncluida,
    i.is_disabled AS EstaDeshabilitado
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
    ON ic.object_id = i.object_id
   AND ic.index_id = i.index_id
INNER JOIN sys.columns AS c
    ON c.object_id = ic.object_id
   AND c.column_id = ic.column_id
LEFT JOIN sys.key_constraints AS kc
    ON kc.parent_object_id = i.object_id
   AND kc.unique_index_id = i.index_id
WHERE i.object_id = @IdTabla
  AND i.index_id > 0
  AND i.is_hypothetical = 0
ORDER BY
    i.index_id,
    ic.is_included_column,
    ic.key_ordinal,
    ic.index_column_id;

Parameters or permissions

A heap is a table without a clustered index. A hypothetical index retains a definition for estimation but has no usable structure; a materialized index occupies storage and can answer queries.

Change @Tabla to a two-part name. The account needs visibility on the table. The query ignores the heap (index_id = 0) and hypothetical indexes; focuses on materialized indexes with registered columns.

How to read the result

The leaf level is the final part of an index where its rows or locators are stored. An included column is kept there without participating in search order.

Each index occupies one or more rows. Key columns have OrdenLlave greater than zero. Your position matters: an index on (IdCliente, FechaPedido) does not offer exactly the same possibilities as another on (FechaPedido, IdCliente).

EsIncluida = 1 identifies a column that does not participate in the key order, but is stored at the leaf level to cover certain queries. In those rows OrdenLlave appears as zero.

EsUnico = 1 prevents duplicates in the index key, subject to the behavior of NULL and, if it exists, Filtro. The Clasificacion column separates that property from a declared primary key or unique constraint.

A filtered index only contains rows that satisfy Filtro. A disabled index retains its definition, but is not available to maintain or answer queries until it is rebuilt.

What this does establish

It allows you to describe visible keys and indexes of the table, their classification, order, address, included columns, uniqueness, filter and state. It also allows you to check whether a declared constraint is supported by the expected index.

What this still does not establish

Fragmentation describes how dispersed the physical page order is. An execution plan describes the strategy chosen by SQL Server for a query.

The existence of an index does not prove that a query uses it, that its order is appropriate, or that its maintenance cost is justified. The absence of a primary key also does not necessarily mean that another business uniqueness rule does not exist.

This query does not measure reads, writes, fragmentation, or execution plans. It also does not recommend creating or dropping indexes alone.

Next reasonable check

It relates each index to the queries that are actually executed. Then review plans, usage statistics, and modification volume before proposing changes. If an integrity rule is missing, first validate existing data and expected system behavior.

Path index

Development and exploration

22 articles in Development and exploration
  1. 01Search for a table or view by name
  2. 02Search for a column across the database
  3. 03Search for text inside procedures, views, and functions
  4. 04Inspect the dependencies of an object
  5. 05List relationships and foreign keys
  6. 06Inspect primary keys, unique constraints, and indexes
  7. 07List columns with type, length, nullability, and default value
  8. 08Find triggers and their associated tables
  9. 09Find recently modified objects
  10. 10Get approximate row counts for every table
  11. 11Measure the space used by each table
  12. 12Inspect IDENTITY columns and their current value
  13. 13Find duplicate rows by business key
  14. 14Find orphaned rows
  15. 15Compare two data sets
  16. 16Validate conversions before changing a column
  17. 17Run an UPDATE with controlled validation and rollback
  18. 18Run a DELETE with a logical backup and controlled rollback
  19. 19Compare the structure of two databases
  20. 20Describe the result of a query or stored procedure
  21. 21Find tables without a primary key
  22. 22Search selected columns for a value