Choosing the Right ON DELETE and ON UPDATE Actions in MySQL Foreign Keys

Choosing the Right ON DELETE and ON UPDATE Actions in MySQL Foreign Keys
Photo by Yuheng Ouyang / Unsplash

When defining foreign key constraints in MySQL (or MariaDB), two clauses often confuse developers: ON DELETE and ON UPDATE. These determine what happens when a referenced row in a parent table (for example, users) is deleted or updated while child rows in another table (for example, user_details) depend on it. Choosing the right option is critical for maintaining data integrity and ensuring your application behaves as expected.


The Basics of Foreign Keys

A foreign key creates a link between two tables, enforcing that a value in one table must exist in another. In your case:

  • user_details.user_id references users.id.
  • This enforces that every user_details row must belong to an existing users row.

But what happens if:

  1. A user is deleted from the users table?
  2. A user’s ID is updated in the users table?

That’s where ON DELETE and ON UPDATE come in.


Options for ON DELETE and ON UPDATE

1. RESTRICT (your current setup)

  • Behavior: Prevents deletion or updating if related rows exist in the child table.
  • Use case: When you want to enforce strict referential integrity. For example, you cannot delete a user until all related user_details are manually removed.
  • Pros: Safe and prevents accidental data loss.
  • Cons: Requires manual cleanup before deleting parent records.

2. CASCADE

  • Behavior: Automatically deletes or updates child rows when the parent row is deleted or updated.
  • Use case: When child rows should not exist independently. For example, deleting a user should also delete their user_details.
  • Pros: Keeps tables clean without manual intervention.
  • Cons: Dangerous if you accidentally delete a parent row—you could wipe out many related rows.

3. SET NULL

  • Behavior: Sets the foreign key in the child table to NULL when the parent row is deleted or updated.
  • Use case: When child data can remain even if the parent is gone. For example, an audit log might keep records of user_details, but if the user is deleted, the link is removed by setting user_id = NULL.
  • Pros: Preserves child data.
  • Cons: Requires the child column (user_id) to allow NULL.

4. NO ACTION

  • Behavior: Similar to RESTRICT, but enforcement is checked at the end of the SQL statement, not immediately.
  • Use case: Rarely used; functionally similar to RESTRICT in MySQL.

Practical Recommendations

For a one-to-one relationship like usersuser_details:

  • If user_details should always be removed when the user is deleted → ON DELETE CASCADE.
  • If you want to keep user_details only if the user exists → ON DELETE RESTRICT.
  • If you want to keep orphan details but not linked → ON DELETE SET NULL.

For ON UPDATE, CASCADE is often safer than RESTRICT, but since primary keys (id) almost never change, most systems just use RESTRICT.


Additional Considerations

  1. Business Logic First
    Database constraints enforce rules, but the real decision should come from your business rules. For example:
    • Do you want historical records preserved even if the user is deleted?
    • Do you want deletions to cascade automatically?
  2. Soft Deletes
    Many applications use a soft delete pattern (a column like deleted_at instead of physical deletion). In this case, ON DELETE CASCADE won’t trigger because you aren’t actually deleting the row.
  3. Performance Impact
    • CASCADE can impact performance if large numbers of rows are deleted.
    • RESTRICT forces extra checks on every delete or update.
  4. Developer Safety
    Using CASCADE is convenient but can be risky in production systems where a mistaken delete statement could wipe large amounts of data.

Finally

Choosing between RESTRICT, CASCADE, and SET NULL is not just a technical decision—it’s a business rule decision.

  • Use RESTRICT when you want strict control.
  • Use CASCADE when the child data should never outlive the parent.
  • Use SET NULL when the child can exist without the parent.

For your case of usersuser_details, the most common approach is:

  • ON DELETE CASCADE (because user details should not exist without a user).
  • ON UPDATE RESTRICT (because user IDs almost never change).

Support Us