# Impact Analysis

> See what a schema migration costs before it runs: data loss, statements that fail on existing rows, table rewrites, locks, and dependent views. Works on migration files too.

Source: https://schemity.com/doc/impact-analysis/

**Impact analysis** ([what it catches, with real cases](https://schemity.com/schema-migration-impact-analysis/)) tells you what a pending schema migration costs before you run it: which statements lose data, which will fail on the rows that already exist, which rewrite a whole table, which views, triggers, and functions depend on what changes, and how far the change spreads through foreign keys. It reads the same planner that writes the migration SQL, so the report and the SQL cannot disagree. It is a licensed desktop feature and needs a diagram connected to a live PostgreSQL, MySQL, MariaDB, SQL Server, or SQLite database. [Snowflake](https://schemity.com/doc/snowflake/) diagrams have no live connection, so impact analysis does not apply to them.

Nothing in impact analysis changes your database. It reads the catalog - row estimates, sizes, and the objects that depend on a table - and the only other query it runs is a count, and only when you ask for one.

## How do I open impact analysis?

Edit a connected diagram, then press **F7** or click **Impact** in the control bar. The Impact drawer lists the findings without marking anything on the main diagram; to see the change drawn, use **Preview changes** (below). A badge on the Impact button keeps the count current as you edit.

The same findings also appear in the migration dialog, between the target and the SQL, so they are in front of you at the moment you decide to apply. When there is nothing to report, the dialog says so in one line, so silence reads as a checked result rather than a missing one.

## What does impact analysis report?

Findings are grouped by consequence:

- **Loses data** - dropping a table or a column, or a type change that cannot keep every value, such as text to integer or a narrower `varchar`.
- **Can fail on existing data** - `SET NOT NULL` on a column that has nulls, a new `NOT NULL` column with no default on a table with rows, a unique constraint over duplicates, a foreign key with orphan rows, or a check constraint the existing rows were never tested against.
- **Storage engine** - MySQL only: a foreign key on a table whose engine keeps none, such as MyISAM, is accepted and silently not stored, and a foreign key that references such a table is refused.
- **Holds back other statements** - a change that locks a table while it reads every row: building an index, checking a new key, checking a column for `NULL` before `SET NOT NULL`, or a type change that rewrites. The finding says whether reads wait too or only writes, with the table's size on disk. On PostgreSQL a unique constraint holds back reads and writes while a plain unique index holds back only writes. Tables under 10,000 rows are skipped, since the lock is over before anyone waits on it. SQLite locks the whole database for any write, so it gets no finding here.
- **Rewrites the table** - a type change the engine performs by copying the table: a non-binary-compatible change on PostgreSQL, any `MODIFY` on MySQL, a type change on SQL Server, or SQLite's rebuild path.
- **Dependent objects** - views, materialized views, triggers, functions, procedures, and generated columns that use a table or column you drop, rename, or retype, each with what the change does to it: the database refuses the statement, the object follows the change, it is dropped with the table, or it breaks the next time it runs. The answer depends on the database: a PostgreSQL view blocks a type change of a column it reads, a MySQL view is checked only when it runs, and a SQL Server view created `WITH SCHEMABINDING` blocks even a rename.
- **Spreads through the diagram** - a change to a primary key or unique column that other tables reference, followed through every foreign key that reaches it, with the hop count. The finding names the tables it reaches and the [context views](https://schemity.com/doc/context-views/) they sit in. A foreign key the migration drops only to add it back, for example after its `ON DELETE` or `ON UPDATE` action changes, is reported once as "Drops and recreates a foreign key on `tickets`", not as a new key that could fail on orphan rows, since the rows already satisfied the old one.

Findings are facts with numbers, such as "drops `pwd_histories.pwd`: ~1.2M rows". They are not severity levels, and the report never tells you not to proceed.

## How accurate are the row counts?

The numbers are **catalog estimates** - PostgreSQL's `reltuples`, MySQL's `table_rows`, SQL Server's partition stats, and SQLite's `sqlite_stat1` when `ANALYZE` has run - so reading them never scans a table. An estimate the database does not have is shown as unknown.

When the exact number matters, click **Count exactly** on a finding. Schemity runs one count for that question - the rows, the nulls, the duplicates, or the orphans - with a 60-second timeout, inside a read-only transaction on PostgreSQL and MySQL.

Dependents found by name in a function's body, rather than through the database's dependency catalog, are worded as "mentions" the table, never as "depends on", because a name match is not proof.

## Can I analyze a migration file I did not write in Schemity?

Yes, as SQL. Prisma, Flyway, golang-migrate, and hand-written migrations are SQL files already; for a tool that writes migrations as code, generate the SQL first, for example with `alembic upgrade --sql` or Django's `sqlmigrate`. Press **Shift+F7**, or open the dropdown on the **Impact** button and choose **Analyse a migration file**. The **SQL migration** drawer opens; paste the SQL or open one or more `.sql` files, and Schemity analyzes them against the connected database, with nothing in them ever executed.

Several files are read as one migration, in the order the migration tool runs them: Flyway's `V2` before `V10`, timestamped names in time order, and Prisma's `<timestamp>_<name>/migration.sql` folders by folder. Rollback files - golang-migrate's `*.down.sql` and Flyway's `U2__` undo files - are left out, and each finding names the file its statement came from. Its **Preview changes** button draws the file's table changes the same way as the pending-changes preview below.

The file report adds a **Changes data** group for `UPDATE`, `DELETE`, and `INSERT`: how many rows a statement can touch, with **Count exactly** offered when its `WHERE` clause is simple enough to count safely. A condition containing a function call or a subquery is shown but not counted, so a count can never have a side effect.

Every statement is accounted for. Migration bookkeeping tables such as `_prisma_migrations`, `alembic_version`, and `flyway_schema_history` are skipped, and anything Schemity cannot classify - a `CREATE FUNCTION`, a `DO` block - is listed under **Not analysed** with the reason.

## How does Schemity protect a production database?

Each connection has a setting, **Ask for the database name before applying a migration**, on by default for connections tagged Production. With it on, **Apply SQL statement** stays disabled in the migration dialog until you type the database name. The migration dialog also names its target - host, database, schema, and environment - and a connected diagram's footer shows `host : database`, so it is always clear which database a change would reach.

## Can I see what changed before I apply it?

Yes, drawn. **Preview changes** in the Impact drawer's header opens your pending changes as a change preview: a throwaway, read-only canvas showing only the tables the migration touches and the neighbours a change reaches through a foreign key. A dropped table has a red border and a created one a green border, dropped, added, and changed columns are tinted red, green, and blue, and a relation that goes away or arrives with a table takes that table's colour. The preview's **Findings** drawer opens with **Planned changes**, the migration in words - tables created, dropped and renamed, then each table's columns renamed, added, altered and dropped - followed by the lint and impact findings for the shown tables.

Inside a change preview, search covers only its tables, and Save, History and undo are blocked. **Close** or **Escape** returns to the diagram, and nothing in the preview is ever saved. An AI agent can open the same kind of picture for a migration file it wrote; see [AI Agents via MCP](https://schemity.com/doc/ai-assisted-design/).

For the edits themselves, press **F6** to open **History**, which describes every undo step and when it was made. Steps staged by an AI agent name the agent that made them, so you can review and undo an agent's edits before the migration exists.

## Next

Generate and apply the change with [Migration SQL Diff](https://schemity.com/doc/migration-sql-diff/).
