The reach of a DELETE is decided by a property that most diagram tools never store and one major database never logs. That property is the referential action - CASCADE, RESTRICT, SET NULL - and it lives on the foreign key constraint, which means it belongs in the ERD every bit as much as the crow’s foot does.
Ask a team what happens when a customer row is deleted and you get confident answers. Ask them to point at where that behavior is written down and the confidence drains. It is not in the diagram on the wiki, because the diagram draws a line between customers and orders and stops there. It is not in the ORM, or at least not reliably - the ORM has its own cascade setting, which may or may not agree with the database’s. It is in the DDL of a constraint someone wrote three years ago, and the only way to know is to go read it.
What actually goes wrong with cascading deletes
The failure mode is not subtle when it happens. In a long-running SQL Server Central thread on whether to use cascade delete, one practitioner explains that he never uses cascade deletes after an experience with “a root delete cascading to an empty database” - a single statement at the top of a dependency tree that emptied everything below it. Another calls the feature “at least as dangerous as it is powerful,” and recommends having proven recovery processes in place before turning it on rather than banning it outright. A third names the real problem precisely: cascade is hidden functionality. The row that disappeared was not named in any statement anyone ran.
The strongest defense in that same thread is also correct - cascades exist because before the standard had them, people implemented the same logic in triggers, inconsistently, and broke referential integrity doing it. Cascade is the right tool for rows that are a logical extension of their parent. Order lines have no meaning without the order. The problem is not the feature. The problem is that the decision to use it is invisible everywhere except the DDL.
And on MySQL, invisible is close to literal. A February 2026 analysis of InnoDB’s cascade blind spot walks through the consequence: InnoDB handles cascade enforcement entirely inside the storage engine, so the child deletions never surface at the SQL layer. In row-based binlog with full image, only the parent deletion is recorded - the child table deletions are nowhere to be found. Audit plugins, including MySQL Enterprise Audit and the Percona Audit Log, miss them for the same reason. Triggers on the child table do not fire; the MySQL manual says so outright, and it has been tracked as bug #11472 since 2005. The article notes MySQL 9.6, released in January 2026, finally moves cascade handling to the SQL layer - which is welcome, and does nothing for the 8.x server you are running today.
So on the most widely deployed configuration of one of the most widely deployed databases, a cascading delete is a set of rows that vanish without appearing in the binlog, the audit log, or any trigger. The only place that behavior is declared is the schema. Which is exactly the artifact your ERD is supposed to be a readable rendering of.
How do I see what ON DELETE CASCADE will actually delete?
You trace the foreign keys outward from the table you are deleting from, following every child relationship whose action is CASCADE, and then follow the children’s children. That traversal is the answer, and it is the thing an entity-relationship diagram is uniquely good at showing - if the diagram carries the actions. In Schemity it does, and it draws them: any relationship set to ON DELETE CASCADE gets a bold crow’s foot at the child end, so the cascading edges of a table stand out from the ordinary ones without opening a single dialog.
This is why referential actions are not a detail to leave in the DDL. Cardinality tells you whether a customer can have many orders. The referential action tells you what happens to those orders when the customer is gone. Both are facts about the same foreign key constraint, and a diagram that renders one while dropping the other is doing half of its job:
| Question about a foreign key | Where the answer lives | Typical diagram | Schemity |
|---|---|---|---|
| One or many on each side? | Whether the key is unique | Crow’s foot notation | Crow’s foot notation |
| Optional or mandatory? | Whether the key column is nullable | Usually an optionality circle | Optionality circle |
| What happens when the parent row is deleted? | ON DELETE on the constraint | Rarely shown - dropped on import | Bold crow’s foot when set to CASCADE |
| What happens when the parent key changes? | ON UPDATE on the constraint | Rarely shown - same | Set and read on the relationship |
The third and fourth rows are where diagrams quietly lose information. A tool that imports a foreign key as “source column, target column, done” has already discarded the deletion semantics of the schema, and no amount of re-arranging the picture brings them back. It is the same class of loss as drawing a 1:1 relationship as 1:N because the unique constraint was ignored: the tool read the key and skipped the constraint’s properties.
Referential actions belong on the relationship, not in a text file
In Schemity, ON DELETE and ON UPDATE are set in the relation dialog alongside the relation type, with the full set of actions available - CASCADE, SET NULL, RESTRICT, NO ACTION, SET DEFAULT. They are properties of the relationship itself, stored in the diagram’s JSON file next to the cardinality, which means they survive every round trip the diagram makes.
That matters in both directions. When you reverse engineer a live PostgreSQL, MySQL, or SQL Server schema, the foreign keys arrive with their referential actions attached, so the diagram you get back describes deletion behavior your production database already has - including the cascades nobody remembers adding. And when you change one, Schemity generates the migration SQL diff for you to read before anything runs, so switching a relationship from RESTRICT to CASCADE shows up as a reviewable statement rather than a checkbox someone ticked. Since the diagram is a plain JSON file, the same change also shows up as a line in a pull request diff, which is the second place a teammate can catch it.
Stored is necessary but not sufficient - a property you have to open a dialog to read is still effectively hidden in a diagram of eighty tables. So a relationship whose ON DELETE is CASCADE is drawn with a bold crow’s foot at the child end. Ordinary relationships keep the normal weight; the cascading ones thicken. Scanning a table’s outbound edges tells you immediately which of them will take rows with them.

Read that audit legend and the deletion story tells itself. All three tables - id_token_logs, pwd_histories, member_logs - hang off parents that live outside this group, and every one of those incoming relationships ends in a bold crow’s foot. Deleting a member takes their password history and their event log with it, by design, and the diagram says so before anyone opens a constraint definition. It also shows how easily a cascade leaves its neighborhood - nothing inside the audit group is deleted directly, since the trigger for all three is a DELETE somewhere else entirely.
The signal is weight rather than color, and that is deliberate. Red would read as an error, and an error is something a designer feels obliged to fix - which is wrong here, because ON DELETE CASCADE on order lines under an order is a correct, considered decision. Bold says this one needs special care, not this one is broken. If you have already decided the cascade is right, you leave it, and the bold end remains as a note to the next person reading the diagram that a delete here is not local.
Tracing a cascade chain is then an ordinary reading task on the canvas. Click a relationship and Schemity highlights the whole line together with the foreign key field on the child and the primary key field on the parent, so which line goes where is never a guess even in a dense diagram. Set a color on the parent entity and every relationship originating from it inherits that color, so the outbound edges of the table you are about to delete from are visually grouped. When you want the exact text, right-click an entity or a legend to export its SQL and read the constraint definitions directly - no information_schema query, no clicking through five property dialogs.
Cascade chains cross bounded contexts, and that is where they get expensive
A cascade that stays inside one subject area is usually the intended kind. The dangerous kind leaves the neighborhood - a delete in the tenancy area that reaches into billing, or an identity cleanup that silently removes audit rows in a completely different module. Those are precisely the chains nobody traces, because nobody has the whole dependency structure in view at once.
A context view is a focused subset of the main diagram that shows one part of the model without the rest of the schema around it. When you are inspecting the deletion behavior of a table inside a context view, an orange dot on an entity tells you it still has at least one relationship to an entity that was not imported into this view - which is to say, this table’s cascades may have effects you cannot see from here. A view that quietly hid those edges would let you conclude a delete is contained when it is not, which is the whole argument for why a view owes you back the context it removes.
One level up, the Context Map renders each context view as a node and draws an arrow for every dependency between contexts, with a badge showing how many foreign keys flow in that direction. Because the arrows are derived from real foreign keys, they are the routes a cascade can travel. Double-click an arrow and a panel lists the exact relations behind it - source field, target entity, constraint name - so “billing depends on tenancy through 7 foreign keys” becomes seven named constraints you can go and check the ON DELETE of. Arrow shape encodes health too: a straight arrow is a one-way dependency, a curved one means the two contexts depend on each other, and mutual dependency is where cascade reasoning stops being local. As with the rest of deriving your context map from the foreign keys you already have, none of this is hand-maintained - it is the schema, read at a different zoom level.
Soft deletion does not remove the problem, it moves it
The usual reaction to cascade anxiety is to stop deleting rows: add deleted_at, filter every query, keep everything. It is a legitimate choice, and it comes with its own bill. In a widely discussed argument that soft deletion probably is not worth it, the author - who worked at Heroku and Stripe - reports that in ten plus years, never once did anyone at any of those places actually use soft deletion to undelete something, while every SELECT in the codebase grew a deleted_at IS NULL predicate that is dangerous to forget. And when a real hard-deletion requirement arrives, the query that implements it is described as five times longer than the illustrated version and spanning a full 30 separate tables - the same cascade traversal as before, now written by hand in application code.
Soft deletion also changes what your constraints mean. A parent marked deleted still satisfies every foreign key pointing at it, so the database now believes in relationships the application considers dead. Uniqueness inverts too: UNIQUE (email) counts soft-deleted rows, so a user who deletes their account cannot sign up again with the same address unless the rule becomes unique-among-live-rows. That is a composite unique constraint or a partial unique index, not a single-column one, and it is exactly the kind of rule that deserves a named, colored badge on the diagram rather than a comment in a migration file - the same reason check constraints belong in the ERD instead of hiding your domain model.
The point is not that soft deletion is wrong. It is that hard delete and soft delete are two different sets of rules about what happens when a row dies, and both of them are expressed in constraints. Whichever you choose, the choice should be legible in the model.
Deletion semantics are part of the model
An ERD is supposed to be the single source of truth for the schema - one artifact that answers structural questions without anyone having to open a SQL console. That promise only holds if the diagram carries the facts the schema carries. Cardinality without referential actions leaves the most consequential property of a foreign key sitting in DDL nobody reads, on an engine that will not log it when it fires.
A desktop ERD tool that reads referential actions out of the live database, keeps them on the relationship, draws the cascading ones so they stand out, writes changes back through a reviewed migration, and stores the whole thing as JSON in your Git repository turns “what does this delete take with it” into something you can answer by looking - and something a reviewer can catch before it ships. That is a lower bar than it sounds like, and most tools still do not clear it.
Frequently asked questions
Where is ON DELETE CASCADE stored in a database schema?
It is a property of the foreign key constraint, not of the table or the column. You can read it back from information_schema.referential_constraints in MySQL and PostgreSQL, or from the constraint definition in pg_constraint. Because it lives on the constraint, a tool that imports only the key's source and target columns loses it silently.
Should I use ON DELETE CASCADE or RESTRICT?
Use CASCADE when the child rows are a logical extension of the parent and have no meaning without it - order lines under an order, or address rows under a customer. Use RESTRICT or NO ACTION when the child row is an independent record that someone would want to keep or be warned about, such as an invoice or an audit entry. The test is whether a person deleting the parent would be surprised to lose the child.
Does ON DELETE CASCADE fire triggers on the child table?
In MySQL with InnoDB it does not. The MySQL manual states that cascaded foreign key actions do not activate triggers, and the behavior has been tracked as bug #11472 since 2005. PostgreSQL does fire row-level triggers on cascaded deletes, so any assumption you carry between the two engines needs rechecking.