11 Ways to Retrieve a Primary Key in SQL Server (T-SQL Examples)

There are often many ways to do the same (or similar) thing in SQL Server. Retrieving all primary keys from a database (or even just the primary key for a given table) is a case in point.

This article presents eleven different approaches to retrieving primary key information in SQL Server.

Some of these will be more useful than others, depending on your exact situation.

Example 1 – The sp_pkeys System Stored Procedure

If you just want the primary key for a given table, the sp_pkeys system stored procedure is probably the quickest and easiest way to do it. To do this, simply pass the table name to the procedure.

Like this:

EXEC sp_pkeys Artists;

Result (using vertical output):

TABLE_QUALIFIER | Music
TABLE_OWNER     | dbo
TABLE_NAME      | Artists
COLUMN_NAME     | ArtistId
KEY_SEQ         | 1
PK_NAME         | PK__Artists__25706B50FCD918B1

In this case I requested the primary key from the Artists table. It returns the primary key name (PK__Artists__25706B50FCD918B1), the database name (Music), the table name, the column name (ArtistId), plus other information.

Alternatively, I could have explicitly specified all arguments to get the same result:

EXEC sp_pkeys 
  @table_name = Artists,
  @table_owner = dbo,
  @table_qualifier = Music;

Example 2 – The KEY_COLUMN_USAGE System View

The INFORMATION_SCHEMA.KEY_COLUMN_USAGE system view returns one row for each column that is constrained as a key in the current database. Therefore you’ll also get foreign keys returned with this view.

SELECT 
  TABLE_NAME, 
  COLUMN_NAME,
  CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE;

Result:

+--------------+---------------+-------------------------------+
| TABLE_NAME   | COLUMN_NAME   | CONSTRAINT_NAME               |
|--------------+---------------+-------------------------------|
| Albums       | ArtistId      | FK_Albums_Artists             |
| Albums       | GenreId       | FK_Albums_Genres              |
| Albums       | AlbumId       | PK__Albums__97B4BE379FC780BD  |
| Artists      | ArtistId      | PK__Artists__25706B50FCD918B1 |
| Genres       | GenreId       | PK__Genres__0385057E88BB96F8  |
+--------------+---------------+-------------------------------+

We can see that this returns constraints from all tables. We could also add a WHERE clause to narrow the results to a specific table.

SELECT 
  TABLE_NAME,
  COLUMN_NAME,
  CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'Artists';

Result:

+--------------+---------------+-------------------------------+
| TABLE_NAME   | COLUMN_NAME   | CONSTRAINT_NAME               |
|--------------+---------------+-------------------------------|
| Artists      | ArtistId      | PK__Artists__25706B50FCD918B1 |
+--------------+---------------+-------------------------------+

In these examples, I narrowed the results to just those columns of the view that I’m interested in, but you can also use the asterisk (*) wildcard to return all columns.

SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'Artists';

Result (using vertical output):

CONSTRAINT_CATALOG | Music
CONSTRAINT_SCHEMA  | dbo
CONSTRAINT_NAME    | PK__Artists__25706B50FCD918B1
TABLE_CATALOG      | Music
TABLE_SCHEMA       | dbo
TABLE_NAME         | Artists
COLUMN_NAME        | ArtistId
ORDINAL_POSITION   | 1

Example 3 – The CONSTRAINT_COLUMN_USAGE System View

This is similar to the previous example. The INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE system view returns one row for each column in the current database that has a constraint defined on the column. As with INFORMATION_SCHEMA.KEY_COLUMN_USAGE you’ll also get foreign keys returned with this view. However, INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE is not limited to just key constraints.

SELECT 
  TABLE_NAME, 
  COLUMN_NAME,
  CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE;

Result:

+--------------+---------------+-------------------------------+
| TABLE_NAME   | COLUMN_NAME   | CONSTRAINT_NAME               |
|--------------+---------------+-------------------------------|
| Albums       | ArtistId      | FK_Albums_Artists             |
| Albums       | GenreId       | FK_Albums_Genres              |
| Albums       | AlbumId       | PK__Albums__97B4BE379FC780BD  |
| Artists      | ArtistId      | PK__Artists__25706B50FCD918B1 |
| Genres       | GenreId       | PK__Genres__0385057E88BB96F8  |
+--------------+---------------+-------------------------------+

You can use a WHERE clause to return only a specific table or column:

SELECT 
  TABLE_NAME, 
  COLUMN_NAME,
  CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
WHERE COLUMN_NAME = 'ArtistId';

Result:

+--------------+---------------+-------------------------------+
| TABLE_NAME   | COLUMN_NAME   | CONSTRAINT_NAME               |
|--------------+---------------+-------------------------------|
| Albums       | ArtistId      | FK_Albums_Artists             |
| Artists      | ArtistId      | PK__Artists__25706B50FCD918B1 |
+--------------+---------------+-------------------------------+

In this case I specified a column name, so it returned all constraints on columns with that name. As it turns out, the database uses the same column name in two different tables. One has a foreign key constraint, the other a primary key. Therefore both rows are returned.

Again, you can use the asterisk (*) wildcard to return all columns.

SELECT *
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
WHERE TABLE_NAME = 'Artists';

Result (using vertical output):

TABLE_CATALOG      | Music
TABLE_SCHEMA       | dbo
TABLE_NAME         | Artists
COLUMN_NAME        | ArtistId
CONSTRAINT_CATALOG | Music
CONSTRAINT_SCHEMA  | dbo
CONSTRAINT_NAME    | PK__Artists__25706B50FCD918B1

In this example I limited the output to just constraints on a specific table.

Example 4 – The CONSTRAINT_TABLE_USAGE System View

The INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE system view returns one row for each table in the current database that has a constraint defined on the table.

SELECT 
  TABLE_NAME,
  CONSTRAINT_NAME 
FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE;

Result:

+--------------+-------------------------------+
| TABLE_NAME   | CONSTRAINT_NAME               |
|--------------+-------------------------------|
| Artists      | PK__Artists__25706B50FCD918B1 |
| Genres       | PK__Genres__0385057E88BB96F8  |
| Albums       | PK__Albums__97B4BE379FC780BD  |
| Albums       | FK_Albums_Artists             |
| Albums       | FK_Albums_Genres              |
+--------------+-------------------------------+

This view doesn’t return the column name/s that the constraints are used on, so it might not be as useful for you, depending on your requirements.

Here’s an example that shows all data returned by this view for the Genres table:

SELECT * 
FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE
WHERE TABLE_NAME = 'Genres';

Result:

TABLE_CATALOG      | Music
TABLE_SCHEMA       | dbo
TABLE_NAME         | Genres
CONSTRAINT_CATALOG | Music
CONSTRAINT_SCHEMA  | dbo
CONSTRAINT_NAME    | PK__Genres__0385057E88BB96F8

Example 5 – The TABLE_CONSTRAINTS System View

The INFORMATION_SCHEMA.TABLE_CONSTRAINTS system view returns one row for each table constraint in the current database.

SELECT 
  TABLE_NAME,
  CONSTRAINT_NAME,
  CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS;

Result:

+--------------+-------------------------------+-------------------+
| TABLE_NAME   | CONSTRAINT_NAME               | CONSTRAINT_TYPE   |
|--------------+-------------------------------+-------------------|
| Artists      | PK__Artists__25706B50FCD918B1 | PRIMARY KEY       |
| Genres       | PK__Genres__0385057E88BB96F8  | PRIMARY KEY       |
| Albums       | PK__Albums__97B4BE379FC780BD  | PRIMARY KEY       |
| Albums       | FK_Albums_Artists             | FOREIGN KEY       |
| Albums       | FK_Albums_Genres              | FOREIGN KEY       |
+--------------+-------------------------------+-------------------+

This view doesn’t provide the column name, but it does provide the constraint type. Therefore, you can narrow the results to just primary keys:

SELECT 
  TABLE_NAME,
  CONSTRAINT_NAME,
  CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'PRIMARY KEY';

Result:

+--------------+-------------------------------+-------------------+
| TABLE_NAME   | CONSTRAINT_NAME               | CONSTRAINT_TYPE   |
|--------------+-------------------------------+-------------------|
| Artists      | PK__Artists__25706B50FCD918B1 | PRIMARY KEY       |
| Genres       | PK__Genres__0385057E88BB96F8  | PRIMARY KEY       |
| Albums       | PK__Albums__97B4BE379FC780BD  | PRIMARY KEY       |
+--------------+-------------------------------+-------------------+

Here’s an example that uses a wildcard (*) character to return all data for constraints on the Artists table:

SELECT *
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'Artists';

Result:

CONSTRAINT_CATALOG | Music
CONSTRAINT_SCHEMA  | dbo
CONSTRAINT_NAME    | PK__Artists__25706B50FCD918B1
TABLE_CATALOG      | Music
TABLE_SCHEMA       | dbo
TABLE_NAME         | Artists
CONSTRAINT_TYPE    | PRIMARY KEY
IS_DEFERRABLE      | NO
INITIALLY_DEFERRED | NO

Example 6 – The key_constraints System View

The sys.key_constraints system view contains a row for each object that is a primary key or unique constraint. Therefore we could use the following code to return all primary key constraints in the database, along with the relevant table for each primary key:

SELECT 
  OBJECT_NAME(parent_object_id) AS 'Table',
  name
FROM sys.key_constraints 
WHERE type = 'PK';

Result:

+---------+-------------------------------+
| Table   | name                          |
|---------+-------------------------------|
| Artists | PK__Artists__25706B50FCD918B1 |
| Genres  | PK__Genres__0385057E88BB96F8  |
| Albums  | PK__Albums__97B4BE379FC780BD  |
+---------+-------------------------------+

Of course, you can remove the WHERE clause to return both primary keys and unique keys.

The previous example uses the OBJECT_NAME() function to return the parent table name, rather than its object_id. If I didn’t use the OBJECT_NAME() function, the result would’ve looked like this:

SELECT 
  parent_object_id,
  name
FROM sys.key_constraints
WHERE type = 'PK';

Result:

+--------------------+-------------------------------+
| parent_object_id   | name                          |
|--------------------+-------------------------------|
| 885578193          | PK__Artists__25706B50FCD918B1 |
| 917578307          | PK__Genres__0385057E88BB96F8  |
| 949578421          | PK__Albums__97B4BE379FC780BD  |
+--------------------+-------------------------------+

The parent_object_id is returned instead of the parent object’s name. The OBJECT_NAME() function makes it much easier for us to understand which table each key is applied to.

Here’s an example where I use the wildcard (*) character to return all columns for the primary key on the Albums table:

SELECT *
FROM sys.key_constraints
WHERE type = 'PK'
AND OBJECT_NAME(parent_object_id) = 'Albums';

Result:

name                | PK__Albums__97B4BE379FC780BD
object_id           | 965578478
principal_id        | NULL
schema_id           | 1
parent_object_id    | 949578421
type                | PK
type_desc           | PRIMARY_KEY_CONSTRAINT
create_date         | 2019-05-02 12:56:06.377
modify_date         | 2019-05-02 12:56:06.377
is_ms_shipped       | 0
is_published        | 0
is_schema_published | 0
unique_index_id     | 1
is_system_named     | 1
is_enforced         | 1

Example 7 – The sys.objects System View

The sys.objects system view contains a row for each user-defined, schema-scoped object that is created within a database. So it contains our tables and primary keys.

We can take the previous query and replace key_constraints with objects and we’ll get the same result:

SELECT 
  OBJECT_NAME(parent_object_id) AS 'Table',
  name
FROM sys.objects 
WHERE type = 'PK';

Result:

+---------+-------------------------------+
| Table   | name                          |
|---------+-------------------------------|
| Artists | PK__Artists__25706B50FCD918B1 |
| Genres  | PK__Genres__0385057E88BB96F8  |
| Albums  | PK__Albums__97B4BE379FC780BD  |
+---------+-------------------------------+

Here’s an example that returns all data for the PK__Genres__0385057E88BB96F8 primary key:

SELECT *
FROM sys.objects
WHERE name = 'PK__Genres__0385057E88BB96F8';

Result (using vertical output):

name                | PK__Genres__0385057E88BB96F8
object_id           | 933578364
principal_id        | NULL
schema_id           | 1
parent_object_id    | 917578307
type                | PK
type_desc           | PRIMARY_KEY_CONSTRAINT
create_date         | 2019-05-02 12:56:06.110
modify_date         | 2019-05-02 12:56:06.110
is_ms_shipped       | 0
is_published        | 0
is_schema_published | 0

Example 8 – The REFERENTIAL_CONSTRAINTS System View

This one’s a bit of a stretch, but it could be useful depending on your situation. And technically, it does return (some) primary keys.

The INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS system view returns one row for each FOREIGN KEY constraint in the current database. So its purpose is actually to return foreign keys – not primary keys. However, this view can be handy if you only want to see all primary keys that have an associated foreign key. It’s not a reliable method to use if you want to see all primary keys regardless of whether there’s an associated foreign key.

Here’s an example:

SELECT 
    CONSTRAINT_NAME,
    UNIQUE_CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS;

Result:

+-------------------+-------------------------------+
| CONSTRAINT_NAME   | UNIQUE_CONSTRAINT_NAME        |
|-------------------+-------------------------------|
| FK_Albums_Artists | PK__Artists__25706B50FCD918B1 |
| FK_Albums_Genres  | PK__Genres__0385057E88BB96F8  |
+-------------------+-------------------------------+

As you can see, this only returns those primary keys that have an associated foreign key. To be more precise, it’s actually the other way around – it returns all foreign keys, along with their associated UNIQUE constraint.

So if we compare these results with the results from previous examples, we can see that only two primary keys are returned (compared to three from previous examples). This is because one of the primary keys in this database (PK__Albums__97B4BE379FC780BD) doesn’t have an associated foreign key.

Here’s an example that returns all data for foreign keys of which PK__Artists__25706B50FCD918B1 is the UNIQUE constraint:

SELECT *
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE UNIQUE_CONSTRAINT_NAME = 'PK__Artists__25706B50FCD918B1';

Result:

CONSTRAINT_CATALOG        | Music
CONSTRAINT_SCHEMA         | dbo
CONSTRAINT_NAME           | FK_Albums_Artists
UNIQUE_CONSTRAINT_CATALOG | Music
UNIQUE_CONSTRAINT_SCHEMA  | dbo
UNIQUE_CONSTRAINT_NAME    | PK__Artists__25706B50FCD918B1
MATCH_OPTION              | SIMPLE
UPDATE_RULE               | NO ACTION
DELETE_RULE               | NO ACTION

In this case, only one foreign key is returned.

As mentioned, this view is mainly for returning foreign key information, so only use this method if you want to see foreign key information along with the associated primary key. Don’t use this method if you want to see all primary keys regardless of whether they have an associated foreign key.

Example 9 – The sp_fkeys System Stored Procedure

This method is similar to the previous method, in the sense that it returns foreign key information. It also returns the primary key associated with each foreign key.

Example:

EXEC sp_fkeys Artists;

Result (using vertical output):

PKTABLE_QUALIFIER | Music
PKTABLE_OWNER     | dbo
PKTABLE_NAME      | Artists
PKCOLUMN_NAME     | ArtistId
FKTABLE_QUALIFIER | Music
FKTABLE_OWNER     | dbo
FKTABLE_NAME      | Albums
FKCOLUMN_NAME     | ArtistId
KEY_SEQ           | 1
UPDATE_RULE       | 1
DELETE_RULE       | 1
FK_NAME           | FK_Albums_Artists
PK_NAME           | PK__Artists__25706B50FCD918B1
DEFERRABILITY     | 7

I could have used the following syntax instead (or a combination of these arguments):

EXEC sp_fkeys 
  @pktable_name = Artists,
  @pktable_owner = dbo,
  @pktable_qualifier = Music,
  @fktable_name = Albums,
  @fktable_owner = dbo,
  @fktable_qualifier = Music;

As with the previous example, this method should not be relied upon to return all primary keys. If a primary key doesn’t have an associated foreign key, it won’t be returned.

Example 10 – The sp_helpconstraint System Stored Procedure

The sp_helpconstraint system stored procedure returns a list of all constraint types, their user-defined or system-supplied name, the columns on which they have been defined, and the expression that defines the constraint (for DEFAULT and CHECK constraints only).

Therefore you can use it to return information on primary keys in your database.

Example:

EXEC sp_helpconstraint Albums;

Result:

+---------------+
| Object Name   |
|---------------|
| Albums        |
+---------------+
 
-[ RECORD 1 ]-------------------------
constraint_type        | FOREIGN KEY
constraint_name        | FK_Albums_Artists
delete_action          | No Action
update_action          | No Action
status_enabled         | Enabled
status_for_replication | Is_For_Replication
constraint_keys        | ArtistId
-[ RECORD 2 ]-------------------------
constraint_type        |  
constraint_name        |  
delete_action          |  
update_action          |  
status_enabled         |  
status_for_replication |  
constraint_keys        | REFERENCES Music.dbo.Artists (ArtistId)
-[ RECORD 3 ]-------------------------
constraint_type        | FOREIGN KEY
constraint_name        | FK_Albums_Genres
delete_action          | No Action
update_action          | No Action
status_enabled         | Enabled
status_for_replication | Is_For_Replication
constraint_keys        | GenreId
-[ RECORD 4 ]-------------------------
constraint_type        |  
constraint_name        |  
delete_action          |  
update_action          |  
status_enabled         |  
status_for_replication |  
constraint_keys        | REFERENCES Music.dbo.Genres (GenreId)
-[ RECORD 5 ]-------------------------
constraint_type        | PRIMARY KEY (clustered)
constraint_name        | PK__Albums__97B4BE379FC780BD
delete_action          | (n/a)
update_action          | (n/a)
status_enabled         | (n/a)
status_for_replication | (n/a)
constraint_keys        | AlbumId

Example 11 – The sp_help System Stored Procedure

The sp_help system stored procedure reports information about a database object (any object listed in the sys.sysobjects compatibility view), a user-defined data type, or a data type. This includes information about primary keys.

When you run this without any arguments, it provides information about summary information of objects of all types that exist in the current database is returned (i.e. a lot of information).

However, you can provide the name of a table, and it will return information about that table. Again, you’ll get a lot of information returned, but amongst this will be primary key information.

Example:

EXEC sp_help Artists;

Result:

+---------+---------+------------+-------------------------+
| Name    | Owner   | Type       | Created_datetime        |
|---------+---------+------------+-------------------------|
| Artists | dbo     | user table | 2019-05-02 12:56:05.840 |
+---------+---------+------------+-------------------------+
 
-[ RECORD 1 ]-------------------------
Column_name          | ArtistId
Type                 | int
Computed             | no
Length               | 4
Prec                 | 10   
Scale                | 0    
Nullable             | no
TrimTrailingBlanks   | (n/a)
FixedLenNullInSource | (n/a)
Collation            | NULL
-[ RECORD 2 ]-------------------------
Column_name          | ArtistName
Type                 | nvarchar
Computed             | no
Length               | 510
Prec                 |      
Scale                |      
Nullable             | no
TrimTrailingBlanks   | (n/a)
FixedLenNullInSource | (n/a)
Collation            | SQL_Latin1_General_CP1_CI_AS
-[ RECORD 3 ]-------------------------
Column_name          | ActiveFrom
Type                 | date
Computed             | no
Length               | 3
Prec                 | 10   
Scale                | 0    
Nullable             | yes
TrimTrailingBlanks   | (n/a)
FixedLenNullInSource | (n/a)
Collation            | NULL
 
+------------+--------+-------------+-----------------------+
| Identity   | Seed   | Increment   | Not For Replication   |
|------------+--------+-------------+-----------------------|
| ArtistId   | 1      | 1           | 0                     |
+------------+--------+-------------+-----------------------+
 
+-------------------------------+
| RowGuidCol                    |
|-------------------------------|
| No rowguidcol column defined. |
+-------------------------------+
 
+-----------------------------+
| Data_located_on_filegroup   |
|-----------------------------|
| PRIMARY                     |
+-----------------------------+
 
+-------------------------------+---------------------------------------------------+--------------+
| index_name                    | index_description                                 | index_keys   |
|-------------------------------+---------------------------------------------------+--------------|
| PK__Artists__25706B50FCD918B1 | clustered, unique, primary key located on PRIMARY | ArtistId     |
+-------------------------------+---------------------------------------------------+--------------+
 
-[ RECORD 1 ]-------------------------
constraint_type        | PRIMARY KEY (clustered)
constraint_name        | PK__Artists__25706B50FCD918B1
delete_action          | (n/a)
update_action          | (n/a)
status_enabled         | (n/a)
status_for_replication | (n/a)
constraint_keys        | ArtistId
 
+--------------------------------------+
| Table is referenced by foreign key   |
|--------------------------------------|
| Music.dbo.Albums: FK_Albums_Artists  |
+--------------------------------------+

You can also provide the name of a primary key to get some basic information about it:

EXEC sp_help PK__Artists__25706B50FCD918B1;

Result:

+-------------------------------+---------+-----------------+-------------------------+
| Name                          | Owner   | Type            | Created_datetime        |
|-------------------------------+---------+-----------------+-------------------------|
| PK__Artists__25706B50FCD918B1 | dbo     | primary key cns | 2019-05-02 12:56:05.853 |
+-------------------------------+---------+-----------------+-------------------------+