Find tables without primary key

An update needs to identify each row unambiguously, but we don’t know which tables declare a primary key. We also want to locate structures that deserve review before an integration or migration.

What we need to distinguish

A primary key identifies each row uniquely and does not accept NULL. A unique index prevents its value combination from repeating but does not by itself declare the table’s primary identity.

We will search for user tables without a PRIMARY KEY constraint. We will also indicate if there is a unique index, because a table can lack a primary key and still protect a combination of values.

Query

/*
    Antes de ejecutar:
        Revisa la base activa, los nombres de objetos, los parámetros y los permisos.
        Adapta el ejemplo a tu modelo y pruébalo primero en un ambiente controlado.
*/

SET NOCOUNT ON;
GO
-- Consulta
SELECT
    s.name AS Esquema,
    t.name AS Tabla,
    CASE
        WHEN EXISTS
        (
            SELECT 1
            FROM sys.indexes AS i
            WHERE i.object_id = t.object_id
              AND i.is_unique = 1
              AND i.is_hypothetical = 0
        ) THEN 1
        ELSE 0
    END AS TieneIndiceUnico,
    t.create_date AS FechaCreacion,
    t.modify_date AS FechaModificacion
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
    ON s.schema_id = t.schema_id
WHERE t.is_ms_shipped = 0
  AND t.temporal_type <> 1
  AND NOT EXISTS
  (
      SELECT 1
      FROM sys.key_constraints AS kc
      WHERE kc.parent_object_id = t.object_id
        AND kc.type = 'PK'
  )
ORDER BY
    s.name,
    t.name;
GO

Parameters or permissions

The query works on the current basis and requires metadata visibility. temporal_type = 1 identifies a temporary history table; is excluded because these tables have special restrictions and should not be treated as an ordinary keyless table. If you need to check external, memory-optimized tables, graph tables, or other specialized structures, add their flags to the result and evaluate them separately.

How to read the result

Each row represents a table with no PK type restriction. HasUniqueIndex = 1 indicates that at least one unique index exists, but does not claim that its columns form the correct functional identity or that all of them allow the expected behavior with null values. A primary key also creates a unique index to enforce the constraint, although the index can be clustered or nonclustered. Here we query sys.key_constraints to distinguish the declared rule from any unique index. Dates help guide the review, but modify_date can change due to index operations and is not reliable design history.

What this does establish

Allows you to enumerate visible tables that do not declare a primary key and recognize which ones have at least one unique index.

What this still does not establish

A staging table holds preparatory data before validation or transfer to its destination; it may follow different rules from a transactional table.

It does not prove that a table contains duplicates, is poorly designed, or that adding a key is safe. Staging tables, queues or work structures may have specific reasons for not declaring it. It also does not automatically identify the columns that should form the key.

Next reasonable check

Review the purpose and volume of each candidate table. Searches for duplicates on the proposed business key, identifies existing references and checks for nullability before designing a constraint.

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