Saturday, October 4, 2025
HomeHow ToPostgreSQL Foreign Key

PostgreSQL Foreign Key

To create relationships between tables and enforce referential integrity in PostgreSQL, the FOREIGN KEY constraint is your best approach. This key ensures that data remains consistent across related tables and maintains the relational structure of PostgreSQL databases.

Table of contents

This article is the second in our series on PostgreSQL database constraints, following our recent review of Primary Keys. Since both primary keys and foreign keys are fundamental for enforcing data integrity in PostgreSQL tables, understanding how to work with them is essential for effective database design.

In this article, you’ll learn the following topics:

  • Understand the basics of the PostgreSQL foreign key constraint.
  • Create, drop, and manage foreign keys with real-world SQL examples.
  • Work with one of the most convenient PostgreSQL GUI tools for handling foreign keys.
Download Postgres GUI tool

Learn the basics of the PostgreSQL FOREIGN KEY constraint

What is FOREIGN KEY in PostgreSQL?

A foreign key in PostgreSQL is a constraint that links one or more columns in a table (the child or referencing table) to the primary key of another table (the parent or referenced table) and ensures referential integrity between the two. 

The table that contains the foreign key is called the child table, while the table referenced by the foreign key is known as the parent table. A foreign key is sometimes referred to as the referencing key because it matches the primary key of the parent table. In other words, a foreign key field in one table points to the primary key field of another. 

Foreign keys, along with others like primary keys, check constraints, and unique constraints, all belong to the family of PostgreSQL database constraints. These constraints define rules on table columns and prevent invalid or inconsistent data from being inserted into the database. 

How to define a FOREIGN KEY in SQL (Example) 

Here’s how you can create two related tables in PostgreSQL using a foreign key constraint. In this example, there is a sportspeople table, which includes a foreign key column sport_id that references the primary key column sport_id in the sports table. This establishes a one-to-many relationship, meaning one sport can have multiple sportspeople associated with it. 

CREATE TABLE sports ( 
  sport_id INT, 
  sport_name VARCHAR(255) NOT NULL, 
  PRIMARY KEY (sport_id) 
); 
 
CREATE TABLE sportspeople ( 
  sportsperson_id INT, 
  sport_id INT, 
  sportperson_name VARCHAR(255) NOT NULL, 
  PRIMARY KEY (sportsperson_id), 
  CONSTRAINT fk_sport_id 
      FOREIGN KEY (sport_id) 
      REFERENCES sports (sport_id) 
); 
Postgres add foreign key if not exists

Note
A PostgreSQL table can include multiple foreign keys if it needs to reference more than one related table.

Add FOREIGN KEY while creating a table

To define a foreign key during table creation in PostgreSQL, use the CONSTRAINT … FOREIGN KEY … REFERENCES … syntax. You can also add a foreign key later with an ALTER TABLE statement or create it visually using a PostgreSQL GUI tool like dbForge Studio for PostgreSQL. Here is the syntax to define your foreign key. 

PostgreSQL FOREIGN KEY Syntax 

CONSTRAINT [constraint_name] FOREIGN KEY (fk_columns)  
REFERENCES parent_table (parent_table_columns) 
[ON DELETE action] 
[ON UPDATE action] 

In this syntax: 

  • constraint_name represents the name of the foreign key constraint. If not specified, PostgreSQL generates one automatically using its default naming convention.
  • fk_columns is the column(s) in the child table that will act as the foreign key.
  • parent_table is the name of the referenced (parent) table, followed by the column(s) it exposes as the primary key.
  • ON DELETE / ON UPDATE are optional clauses that define what happens when the referenced row in the parent table is deleted or updated. 

Also, PostgreSQL supports the following referential actions: 

  • NO ACTION
  • RESTRICT
  • SET NULL
  • SET DEFAULT
  • CASCADE

Let’s explore each of these actions in more detail below. 

What does NO ACTION mean in PostgreSQL?

The NO ACTION referential action prevents deleting or updating a referenced row if it would violate a foreign key constraint. Instead, PostgreSQL raises an error. This procedure is the default behavior when no other action is specified. 

Here’s how it behaves when you try to delete a referenced row:  

CREATE TABLE sports ( 
    sport_id INT, 
    sport_name VARCHAR(255) NOT NULL, 
    PRIMARY KEY (sport_id) 
); 
 
CREATE TABLE sportspeople ( 
    sportsperson_id INT, 
    sport_id INT, 
    sportperson_name VARCHAR(255) NOT NULL, 
    PRIMARY KEY (sportsperson_id), 
    CONSTRAINT fk_sport_id 
      FOREIGN KEY (sport_id) 
      REFERENCES sports (sport_id) 
      ON DELETE NO ACTION 
); 

If you attempt to delete a row from the parent sports table:

DELETE FROM sports 
WHERE sport_id = 2; 
PostgreSQL constraint - foreign key - example of NO ACTION

PostgreSQL will issue an error because the referenced sport_id still exists in the sportspeople table, and deleting it would break referential integrity.

RESTRICT

In PostgreSQL, the RESTRICT referential action prevents the deletion of a parent row if any child rows reference it, and it does so immediately, without deferral. This means that the check is enforced at the moment the DELETE or UPDATE command is executed, rather than waiting until the end of the transaction. 

For example, if you attempt to run the following command: 

DELETE FROM sports 
WHERE sport_id = 1; 

If there are still rows in the sportspeople table referencing sport_id = 1, PostgreSQL will block the operation and return an error. Here is how the RESTRICT differs from NO ACTION. 

Key difference: 

  • NO ACTION: constraint check can be deferred until the end of the transaction.
  • RESTRICT: constraint check happens immediately and cannot be deferred. 

This method of handling constraints makes RESTRICT slightly stricter than NO ACTION in terms of timing. 

SET NULL

In PostgreSQL, SET NULL is a referential action that sets the foreign key column in the child table to NULL when the referenced parent row is deleted or updated. This ensures that the child record remains in the table, but its link to the parent is cleared. 

Example: If the sportspeople table uses ON DELETE SET NULL for its foreign key and a row is deleted from the sports table, the corresponding sport_id values in sportspeople will automatically become NULL.  

As you can see from the output, the row that had sport_id equal to 1 now has it set to NULL. 

Before deletion: 

sportsperson_idsport_idsportperson_name
John Cena 
Michael Phelps 

After deletion of sport_id = 1 from sports: 

sportsperson_idsport_idsportperson_name
John Cena 
NULL Michael Phelps 

This action is useful when you want to preserve child records but remove their dependency on the deleted or updated parent row.

SET DEFAULT

In PostgreSQL, SET DEFAULT sets the child table’s foreign key column to its defined default value when the referenced row in the parent table is deleted or updated. This action works only if the child column has a default value specified. 

For example, suppose the sportspeople table defines a foreign key on sport_id with ON DELETE SET DEFAULT and a default value of 2. If a row in the parent sports table with sport_id = 1 is deleted, the matching rows in sportspeople will have their sport_id automatically reset to the default value 2. Here is how this works in practice. 

First, let’s create the table:

Create a postgres table with foreign key column

If we now delete the parent row with sport_id = 1:

DELETE FROM sports 
WHERE sport_id = 1; 
 
SELECT * FROM sportspeople; 
Add foreign key postgres - ON DELETE SET DEFAULT result

Before deletion: 

sportsperson_idsport_idsportperson_name
John Cena 
Michael Phelps 

After deletion of sport_id = 1 from sports: 

sportsperson_idsport_idsportperson_name
John Cena 
Michael Phelps 

With ON DELETE SET DEFAULT, the orphaned row is reassigned to the default value (2 in this case). This behavior is useful when you want to redirect orphaned child rows to a fallback reference rather than removing or nullifying the relationship.

CASCADE

The CASCADE referential action automatically deletes or updates child rows when any change is made to the referenced parent row. With ON DELETE CASCADE, removing a row in the parent table also removes all related rows in the child table. With ON UPDATE CASCADE, changes to the parent’s primary key value also result in changes in the child table automatically. 

Here’s an example using ON UPDATE CASCADE: 

First, you’ll create two tables—sports and sportspeople—but this time add a foreign key with ON UPDATE CASCADE.

Create a postgres table and define a foreign key

Now update the sports table, changing the value in the primary key column. 

UPDATE sports 
  SET sport_id = 5 
  WHERE sport_id = 1; 

Here is how this action affects the data in the child sportspeople table. 

PostgreSQL add foreign key with ON UPDATE CASCADE - result demonstration

Before update: 

sportsperson_idsport_idsportperson_name
John Cena 
Michael Phelps 

After update of sport_id = 1 → 5 in sports: 

sportsperson_idsport_idsportperson_name
John Cena 
Michael Phelps 

As shown, the child table automatically reflects the updated key value, preserving referential integrity without manual intervention. 

Add FOREIGN KEY to an existing table

To add a foreign key to an existing PostgreSQL table, use the ALTER TABLE ... ADD CONSTRAINT syntax. This allows you to enforce referential integrity even after the table has already been created. 

For example, here is how to add a foreign key to the orders table that references the customers table: 

ALTER TABLE orders 
    ADD CONSTRAINT fk_orders_customers  
    FOREIGN KEY (customer_id) REFERENCES customers (id); 

If you need to introduce a new column to serve as a foreign key, you can add it first and then define the constraint: 

To add a new column, use the following syntax: 

ALTER TABLE orders 
    ADD COLUMN IF NOT EXISTS city_id INT; 

To add a foreign key on the new column, use the following syntax: 

ALTER TABLE orders  
    ADD CONSTRAINT fk_city_id  
    FOREIGN KEY (city_id) REFERENCES city (id); 

Note: Both adding and dropping foreign keys in PostgreSQL are performed using the ALTER TABLE statement. 

How to drop FOREIGN KEY in PostgreSQL

To remove a foreign key constraint in PostgreSQL, use the ALTER TABLE command followed by DROP CONSTRAINT. This statement detaches the foreign key from the table, meaning the database will no longer enforce the relationship.

Syntax: 

ALTER TABLE table_name  
    DROP CONSTRAINT foreign_key_name; 

What it does: 

  • table_name: the name of the child table that contains the foreign key.
  • foreign_key_name: the name of the foreign key constraint to be removed.

Example: 

The following example demonstrates how to drop the foreign key constraint named fk_sport_id from the sportspeople table: 

After this command, the sportspeople table will no longer enforce a foreign key relationship on the sport_id column. 

ALTER TABLE sportspeople  
    DROP CONSTRAINT fk_sport_id; 

After this command, the sportspeople table will no longer enforce a foreign key relationship on the sport_id column.

Why is a GUI tool a better way to manage keys in PostgreSQL?

Why GUI is better than CLI 

While PostgreSQL offers powerful command-line capabilities, managing keys through SQL commands can be challenging, especially for beginners. The command line requires memorizing syntax, remembering table and column names, and troubleshooting errors when constraints fail. This requirement increases both the learning curve and the risk of mistakes. 

However, one of the easiest ways to manage keys is to use a graphical user interface (GUI) tool. GUI tools simplify this process by providing visual interactions, intuitive menus, and built-in validation. Instead of typing every command manually, you can create, modify, or drop keys with a few clicks.  

One of the most efficient GUI tools is dbForge Studio for PostgreSQL. 

How dbForge Studio solves these problems 

dbForge Studio for PostgreSQL is an advanced GUI IDE designed to streamline database development and administration. It reduces complexity and speeds up common tasks, making it easier for developers, administrators, and analysts to manage keys and constraints without worrying about syntax errors. Here is a breakdown of the key features in dbForge Studio that can help you manage keys efficiently in PostgreSQL. 

Key features for managing keys in dbForge Studio:

  • Instant syntax checking: with dbForge Studio for PostgreSQL, you can easily prevent errors with real-time validation as you write queries.
  • Intelligent code completion: this is one outstanding feature for managing keys in your database. With this, you get context-aware suggestions for tables, columns, and constraints.
  • Master-detail browser: with dbForge Studio for PostgreSQL, you can view and analyze related data across tables in an intuitive interface.
  • Cross-platform convenience: dbForge Studio for PostgreSQL works with PostgreSQL databases on all major operating systems.

dbForge Studio for PostgreSQL eliminates the need to memorize commands and makes key management more efficient, accurate, and user-friendly. 

dbForge Studio for Postgres - Master-Detail Browser

Summary

In summary, PostgreSQL foreign keys are essential for maintaining referential integrity by enforcing relationships between parent and child tables. In this guide, we have explored how to define a foreign key constraint when creating a table with CREATE TABLE or later with ALTER TABLE. We also reviewed how to remove a key using DROP CONSTRAINT. 

Different referential actions, such as CASCADE, SET NULL, SET DEFAULT, RESTRICT, and NO ACTION, determine how updates or deletions in the parent table affect related rows in the child table. For example, CASCADE is useful when child rows should follow parent changes, while SET NULL or SET DEFAULT preserve rows by adjusting values instead. 

By carefully choosing the right action for each relationship, you can ensure that your database remains consistent, synchronized, and free from orphaned records after any operation.

Try before you buy

Download a free 30-day trial of dbForge Studio for PostgreSQL, and see how it can handle your database challenges.

Download dbForge Studio for PostgreSQL
RELATED ARTICLES

Whitepaper

Social

Topics

Products