You Got a Constraint Violation — Let's Fix It Now
I know how annoying this is. You're in the middle of a batch update or inserting a record, and boom — transaction rolls back. Error code like ORA-02291 or SQLSTATE 23503. The database just says "constraint violation" and undoes everything. Let's skip the theory and get to the fix.
The Real Fix: Find the Rogue Data
Most of the time, this error happens because you're trying to insert or update a row that references a parent key that doesn't exist. Or you're inserting a duplicate value where a unique constraint exists. Here's what you do:
- Check the constraint name in the error message. It's usually something like
FK_ORDERS_CUSTOMERorUQ_EMAIL. Write it down. - Run this query to see what the constraint expects (for Oracle):
SELECT constraint_name, constraint_type, table_name, r_constraint_name FROM user_constraints WHERE constraint_name = 'YOUR_CONSTRAINT_NAME'; - For foreign key violations, check the child table for rows that point to a missing parent:
SELECT * FROM child_table WHERE parent_id NOT IN (SELECT id FROM parent_table); - For unique constraint violations, find the duplicate:
SELECT column_name, COUNT(*) FROM your_table GROUP BY column_name HAVING COUNT(*) > 1; - Delete or fix the bad rows. If it's a foreign key issue, either delete the child row or add the missing parent. For duplicates, keep one and remove the others.
I had a client last month whose entire payroll import crashed because someone deleted a department record but left employees pointing to it. Ran the parent check query, found 12 orphans, added the department back. Transaction ran fine after that.
Why This Works (Short Version)
Think of the database like a filing cabinet. A foreign key is like a drawer label — every file has to match a drawer. If a file references a drawer that's gone, the database says "nope" and rolls back everything to keep itself consistent. Unique constraints are like saying "only one file with this name." The rollback is the database's way of protecting data integrity. Find the mismatched data, fix it, and the transaction goes through.
Less Common Variations
1. Check Constraints (Not Just Foreign Keys)
Sometimes the constraint is a CHECK constraint — like a rule that says "salary must be positive." If you try to insert a negative value, rollback happens. The fix is the same: identify the constraint and the bad values. Use SELECT * FROM your_table WHERE salary < 0.
2. Cascading Constraints
If you delete a parent row and the child rows have ON DELETE CASCADE, the delete goes through but might violate another constraint downstream. For example, deleting a customer that has orders that also have line items — but a trigger or another constraint blocks it. In that case, check the error message for the constraint name and trace it back.
3. Trigger-Related Rollbacks
Sometimes the error isn't a constraint but a trigger that raises an exception. Look in the error stack for a trigger name. You might need to comment it out temporarily to test, but better to fix the logic. I've seen triggers that try to validate data across tables and fail silently.
4. Batch Operations with Mixed Data
If you're inserting 1000 rows and one fails, the whole batch might roll back depending on your transaction isolation or error handling. Split the batch into smaller chunks (like 100 rows each) and catch the error per chunk. This lets you isolate the bad row without losing the entire operation.
How to Prevent This in the Future
- Validate data before insert. Run a pre-check query that compares your incoming data against existing constraints. For foreign keys, check
SELECT id FROM parent_table WHERE id IN (your_list)and flag missing ones. - Use transactions with savepoints. If you're using PostgreSQL or Oracle, you can set a savepoint before each insert and rollback to that savepoint instead of the whole transaction. Example:
SAVEPOINT before_insert;thenROLLBACK TO SAVEPOINT before_insert;on error. - Enable constraint deferring. In PostgreSQL and Oracle, you can make constraints
DEFERRABLEso they check at commit time, not at each row. This helps if you need to insert parent and child in the same transaction but out of order. SetSET CONSTRAINTS ALL DEFERRED;at the start. - Log constraint violations. Write a script that catches the error and inserts the bad row into a log table. That way you can fix it later without losing track.
Prevention saves headaches. I've seen teams spend hours replaying failed imports because they didn't validate ahead of time. A one-minute pre-check script would've caught it.