Find foreign keys for a table
Short answer: join information_schema.key_column_usage with information_schema.referential_constraints to get every foreign key with its source column and referenced table. Engine-specific catalogs are often easier to read (Postgres' \d table shows FKs inline; SQL Server's sys.foreign_keys). Useful when figuring out which tables to JOIN against a given table, or when auditing data integrity.
Canonical SQL #
-- Portable: works on Postgres / MySQL / SQL Server / Snowflake / BigQuery.
SELECT
rc.constraint_name,
kcu.table_schema AS source_schema,
kcu.table_name AS source_table,
kcu.column_name AS source_column,
ccu.table_schema AS target_schema,
ccu.table_name AS target_table,
ccu.column_name AS target_column,
rc.update_rule,
rc.delete_rule
FROM information_schema.referential_constraints rc
JOIN information_schema.key_column_usage kcu
ON rc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
ON rc.unique_constraint_name = ccu.constraint_name
WHERE kcu.table_name = 'orders'; -- the child / source table
-- Postgres shortcut: \d orders shows FKs inline.
\d ordersExample #
Common variants #
-- Find tables that reference a given parent (the "what cascades from this?" query).
SELECT
kcu.table_name AS child_table,
kcu.column_name AS child_column,
rc.delete_rule
FROM information_schema.referential_constraints rc
JOIN information_schema.key_column_usage kcu
ON rc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
ON rc.unique_constraint_name = ccu.constraint_name
WHERE ccu.table_name = 'users'; -- parent table
-- Postgres: full FK details using pg_catalog (richer, faster).
SELECT
conrelid::regclass AS child_table,
a.attname AS child_column,
confrelid::regclass AS parent_table,
af.attname AS parent_column,
confdeltype AS on_delete,
confupdtype AS on_update
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)
JOIN pg_attribute af ON af.attrelid = c.confrelid AND af.attnum = ANY(c.confkey)
WHERE c.contype = 'f';
-- SQL Server.
SELECT
OBJECT_NAME(parent_object_id) AS child_table,
OBJECT_NAME(referenced_object_id) AS parent_table,
name AS constraint_name
FROM sys.foreign_keys;Dialect comparison #
| Engine | Syntax | Gotcha |
|---|---|---|
| PostgreSQL | pg_constraint with contype = 'f' | \d table in psql shows FKs inline |
| MySQL | information_schema.referential_constraints | enforced on InnoDB tables only |
| SQL Server | sys.foreign_keys | — |
| BigQuery | information_schema constraint views | FKs are declarative only, not enforced |
| Snowflake | information_schema constraint views | FKs are declarative only, not enforced |
| Oracle | ALL_CONSTRAINTS with CONSTRAINT_TYPE = 'R' | — |
| SQLite | PRAGMA foreign_key_list('orders') | one table at a time; enforcement needs PRAGMA foreign_keys = ON |
Dialect notes #
- Postgres:
\d tableshows incoming and outgoing FKs.pg_constraintis the lower-level catalog with everything;information_schema.referential_constraintsis the portable view. - MySQL:
information_schema.referential_constraintsworks. Note FKs are only enforced on InnoDB tables. - SQL Server:
sys.foreign_keysandsys.foreign_key_columnsare the system views;sp_help table_nameshows constraints. - Snowflake / BigQuery / Redshift: FKs are declarative only — they appear in
information_schemabut are not enforced. Useful for documentation and BI tools (which often use them to suggest joins) but offer no integrity guarantee. - Oracle:
USER_CONSTRAINTS/ALL_CONSTRAINTSfiltered toCONSTRAINT_TYPE = 'R'(referential). - SQLite: no
information_schema.PRAGMA foreign_key_list('table')lists outgoing FKs of a single table.
FAQ #
How do I find all foreign keys for a table?
Join information_schema.referential_constraints with key_column_usage and constraint_column_usage. The portable form returns the source column, target table, target column, and ON DELETE/UPDATE rules. Postgres shortcut: \d table_name in psql shows FKs inline alongside columns.
How do I find tables that reference a specific parent?
Filter key_column_usage (or equivalent) by the target table. The "what cascades from this?" query. Useful before deleting a parent row, or when planning to change a primary key.
How do I know which engines enforce foreign keys?
Postgres / MySQL InnoDB / SQL Server / Oracle / SQLite (with PRAGMA foreign_keys = ON): yes. Snowflake / BigQuery / Redshift: declared but not enforced. MySQL MyISAM: not enforced. The information_schema query returns the constraint metadata regardless of whether enforcement is active.
How can I generate an ERD from foreign key data?
Pull the FK metadata via information_schema, format as DBML, and feed to dbdiagram.io / dbdocs.io. Or generate a Graphviz DOT file from the same data. The diagram updates as the schema changes, with no hand-drawing.
Are foreign keys auto-indexed?
On the parent (PK) side: yes, the primary key index covers it. On the child side: usually NOT. MySQL InnoDB is the exception: it auto-creates an index on the FK column. Other engines need an explicit CREATE INDEX on the FK column for parent-delete queries to perform well.
Why do my schema audits show FKs that don't actually enforce?
On Snowflake / BigQuery / Redshift, FK constraints are declarative metadata only. They appear in information_schema but the engine doesn't enforce them. They're still useful — BI tools (Looker, Tableau) read them to suggest joins; dbt tests can validate referential integrity in your tests rather than runtime.
Use FK introspection to auto-generate JOIN suggestions and schema diagrams. A small script that pulls FK metadata produces a graph of "what connects to what" in your database — feed it to dbdiagram.io, dbdocs.io, or even Graphviz, and you get a wall-poster ERD without hand-drawing one. Even better: for warehouses where FKs aren't enforced, declaring them anyway gives the BI tool (Looker, Tableau, Hex) the join graph it needs to suggest correct joins to analysts. The constraint costs nothing if not enforced; the documentation value is permanent.