Using the ‘key_column_usage’ Table to Get the Primary Key and Foreign Keys in MySQL

In MySQL we can use the information_schema.key_column_usage table to get information about the keys in a table. This table returns one row for each column that is constrained as a key.

We can use this table to find out which column/s is the primary key of a given table and to return any foreign keys from the table.

Example

Here’s an example of querying the information_schema.key_column_usage table to find out which columns are constrained as a key in the specified table:

SELECT *
FROM information_schema.key_column_usage
WHERE table_schema = 'krankykranes'
AND table_name = 'Orders';

Result:

+--------------------+-------------------+---------------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME     | TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | ORDINAL_POSITION | POSITION_IN_UNIQUE_CONSTRAINT | REFERENCED_TABLE_SCHEMA | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME |
+--------------------+-------------------+---------------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+
| def                | krankykranes      | PRIMARY             | def           | krankykranes | Orders     | OrderId     |                1 |                          NULL | NULL                    | NULL                  | NULL                   |
| def                | krankykranes      | FK_Orders_Customers | def           | krankykranes | Orders     | CustomerId  |                1 |                             1 | krankykranes            | Customers             | CustomerId             |
+--------------------+-------------------+---------------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+

I ran this example against the krankykranes database in MySQL, and I checked the Orders table. We can see by the results that the OrderId is the primary key.

We can also see that there’s a foreign key called FK_Orders_Customers. While there’s nothing that explicitly categorises these as the primary key or foreign key, their names and other information in the rows provide a good clue. In MySQL all primary keys are called PRIMARY.

As for the foreign key, it starts with FK_ (which is often the case, unless it was explicitly assigned another name). We can also see the table and columns that the foreign key references.

Composite Primary Key Example

Here’s an example that involves a composite primary key:

SELECT 
    column_name,
    constraint_name,
    ordinal_position
FROM information_schema.key_column_usage
WHERE table_schema = 'krankykranes'
AND table_name = 'orderitems';

Result:

+-------------+------------------------+------------------+
| COLUMN_NAME | CONSTRAINT_NAME        | ORDINAL_POSITION |
+-------------+------------------------+------------------+
| OrderId     | PRIMARY                |                1 |
| OrderItemId | PRIMARY                |                2 |
| OrderId     | FK_OrderItems_Orders   |                1 |
| ProductId   | FK_OrderItems_Products |                1 |
+-------------+------------------------+------------------+

We can see by the results that the OrderId and OrderItemId columns comprise its primary key. We know it’s a composite primary key, because more than one column is listed as PRIMARY (which means that the primary key comprises of two columns, making it a composite key).

Note that the ORDINAL_POSITION column specifies the column’s position within the constraint (not the column’s position within the table).

We can also see that there are two foreign key constraints on this table.

Check the Whole Database

We can remove the table_schema column from our filtering criteria in order to list all tables from the database along with their key constraints.

SELECT 
    table_name,
    column_name,
    constraint_name,
    ordinal_position
FROM information_schema.key_column_usage
WHERE table_schema = 'krankykranes';

Result:

+------------+-------------+------------------------+------------------+
| TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME        | ORDINAL_POSITION |
+------------+-------------+------------------------+------------------+
| Cats       | CatId       | PRIMARY                |                1 |
| Customers  | CustomerId  | PRIMARY                |                1 |
| Customers2 | CustomerId  | PRIMARY                |                1 |
| Dogs       | DogId       | PRIMARY                |                1 |
| Employees  | Id          | PRIMARY                |                1 |
| Idiots     | IdiotId     | PRIMARY                |                1 |
| OrderItems | OrderId     | PRIMARY                |                1 |
| OrderItems | OrderItemId | PRIMARY                |                2 |
| OrderItems | OrderId     | FK_OrderItems_Orders   |                1 |
| OrderItems | ProductId   | FK_OrderItems_Products |                1 |
| Orders     | OrderId     | PRIMARY                |                1 |
| Orders     | CustomerId  | FK_Orders_Customers    |                1 |
| Products   | ProductId   | PRIMARY                |                1 |
| Products   | VendorId    | FK_Products_Vendors    |                1 |
| Vendors    | VendorId    | PRIMARY                |                1 |
+------------+-------------+------------------------+------------------+

Here I included the table_name column in the output so that we could see which table each constraint belongs to.

Check the Whole Server

And going a step further, we can remove the filtering criteria altogether in order to return information about all tables on the server:

SELECT * FROM information_schema.key_column_usage;

I won’t post the results here, as the result set is quite large. In this example I also decided to use the asterisk wildcard (*) to return all rows, but go ahead and choose whichever rows suit your needs.