Oracle User Table Constraints Query with dba_constraints
Oracle Database: Show All Table Constraints for a User using dba_constraints
Purpose
This Oracle SQL query is designed to retrieve a comprehensive list of all constraints associated with tables owned by a specific user in your Oracle database. Constraints are essential rules that ensure data integrity and consistency. This query offers a clear and organized view of these constraints, aiding in database management and troubleshooting.
Constraint metadata lives in three overlapping dictionary views, and knowing which one to reach for matters before the query even runs. USER_CONSTRAINTS shows only the constraints owned by the connected schema. ALL_CONSTRAINTS adds every constraint the connected account has been granted visibility on, whether it owns the table or not. DBA_CONSTRAINTS returns every constraint in the entire database regardless of owner, but only to an account holding a privilege such as SELECT_CATALOG_ROLE or SELECT ANY DICTIONARY. A developer working inside their own schema reaches for USER_CONSTRAINTS. A DBA auditing a specific application account, comparing constraint coverage across two schemas before a migration, or reviewing a third-party schema before granting it production access needs the wider DBA_CONSTRAINTS view with an explicit OWNER filter — which is exactly what the query below does.
DBA_CONSTRAINTS alone answers "does this constraint exist, and what type is it" — it does not answer "what columns does it cover." A full constraint audit typically pairs this query with DBA_CONS_COLUMNS, which maps each constraint name to its underlying column list and column position within a multi-column key. This post focuses on the constraint inventory step: a per-table, per-type breakdown a DBA can scan in seconds before drilling into column-level detail on any constraint that looks wrong.
Sample SQL Command
1set lines 100 pages 999
2break on table_name
3select table_name
4, decode(constraint_type,
5 'C', 'Check',
6 'O', 'R/O View',
7 'P', 'Primary',
8 'R', 'Foreign',
9 'U', 'Unique',
10 'V', 'Check view') type
11, nvl(index_name, R_CONSTRAINT_NAME) "IDX"
12from dba_constraints
13where owner like '&user'
14order by table_name
15, decode(constraint_type,
16 'P','0','R','1','U','2','C','3','O','4','V','5')
17/
Code Breakdown
set lines 100 pages 999: Sets the output formatting for better readability.break on table_name: Organizes the results by grouping them based on table names.select table_name, decode(constraint_type, ... ) type, nvl(index_name, R_CONSTRAINT_NAME) "IDX": Selects three columns:table_name: The name of the table the constraint is applied to.type: Decodes the constraint type code (C,O,P,R,U,V) into human-readable descriptions (Check, Read-Only View, Primary Key, Foreign Key, Unique, Check View)."IDX": Shows the associated index name (if available) or the name of the referenced constraint for foreign keys.
from dba_constraints: Queries thedba_constraintsdata dictionary view, which stores information about all constraints in the database.where owner like '&user': Filters the results to only include constraints for tables owned by the specified user. You'll be prompted to enter the username when running this query.order by table_name, decode(constraint_type, ... ): Sorts the output first by table name and then by constraint type, following a specific priority order (Primary Key, Foreign Key, Unique, Check, Read-Only View, Check View).
The six constraint type codes
CONSTRAINT_TYPE in DBA_CONSTRAINTS is a single-character code, and this query's DECODE translates all six values Oracle defines. P and U are the structural keys — Primary and Unique — each backed by a supporting index. R is a Foreign Key, referencing a primary or unique key on another table (or the same table, for a self-referencing hierarchy). C is a Check constraint, which includes both explicit CHECK (...) clauses and the implicit NOT NULL constraints Oracle generates automatically for NOT NULL column definitions — a detail worth knowing, since a table with no explicit check logic can still show several C rows in the output. O and V are the rarest two: they apply to views rather than base tables — O marks a read-only view constraint, V marks a view created WITH CHECK OPTION. Seeing an O or V row against something you expected to be a plain table is a signal to confirm the object type before investigating further.
Why NVL combines two different columns
INDEX_NAME and R_CONSTRAINT_NAME are populated in mutually exclusive situations, which is what makes wrapping them in a single NVL safe rather than confusing. For a Primary Key or Unique constraint, INDEX_NAME holds the name of the unique index Oracle uses to enforce it, and R_CONSTRAINT_NAME is null. For a Foreign Key, INDEX_NAME is null — a foreign key does not own its own enforcing index — and R_CONSTRAINT_NAME instead holds the name of the parent constraint it references, typically the primary key on the referenced table. NVL(index_name, R_CONSTRAINT_NAME) collapses both cases into a single "IDX" column: for a P or U row you're looking at an index name, and for an R row you're looking at the name of the constraint being referenced. For C, O, and V rows, both source columns are null and the "IDX" column is blank, which is expected.
The DECODE sort priority explained
The second DECODE in the ORDER BY clause assigns each constraint type a sort weight — Primary first (0), then Foreign (1), Unique (2), Check (3), Read-Only View (4), Check view (5) — and Oracle sorts numerically on that weight within each table_name group. This ordering mirrors how a DBA actually reviews a table's constraints: confirm the primary key exists first, check what it's referenced by (foreign keys), then uniques, then the broader set of check conditions, with the rare view-level constraints trailing at the bottom. Without this explicit weighting, the default alphabetical sort on CONSTRAINT_TYPE would put Check (C) before Foreign (R) before Primary (P) — alphabetically correct but operationally backwards for a quick scan.
Key Points
- Data Dictionary View: Leverages the
dba_constraintsview to access constraint metadata. - Constraint Type Decoding: Translates cryptic constraint type codes into meaningful descriptions.
- Index Information: Includes the index name associated with the constraint, if applicable.
- User Filtering: Allows you to focus on constraints related to a specific user's tables.
- Organized Output: Presents the results in a sorted and easy-to-interpret format.
- Note: This query requires you to have the necessary privileges to access the
dba_constraintsview. If you encounter permission issues, consider using theall_constraintsoruser_constraintsviews, which have less restrictive access requirements. - STATUS is not in this query but is worth adding.
DBA_CONSTRAINTS.STATUSreportsENABLEDorDISABLED, andVALIDATEDreportsVALIDATEDorNOT VALIDATED. A constraint can exist, be enabled, and still not be validated against existing data — a state worth knowing about before trusting a foreign key blindly. break on table_nameonly suppresses repeated values, it doesn't group rows physically. The underlyingORDER BY table_nameis what makes the visual grouping possible; removing theORDER BYwhile keeping theBREAKproduces a broken, non-grouped display.
Insights and Best Practices
Understanding the constraints on your tables is crucial for database design, maintenance, and troubleshooting. This query empowers you to quickly identify the types of constraints applied to each table. It helps you pinpoint potential issues like foreign key violations or conflicts with unique constraints.
Add STATUS and VALIDATED for a fuller audit
The base query answers "what constraints exist and what type." It does not answer "are they actually enforcing anything right now." Extending the SELECT list with status and validated turns the same query into a health check: a P or U constraint with STATUS = 'DISABLED' means the structural key isn't currently protecting the table at all, and an R constraint with VALIDATED = 'NOT VALIDATED' means Oracle has not confirmed every existing row satisfies the relationship — a common state right after a bulk load that used ALTER TABLE ... ENABLE NOVALIDATE to skip the validation pass for speed.
Pair with DBA_CONS_COLUMNS for column-level detail
This query is deliberately a table-and-type-level inventory, not a column-level one. Once a specific constraint looks worth investigating — an unexpected U on a table that shouldn't have a uniqueness rule, or an R pointing at a table you didn't know was referenced — joining to DBA_CONS_COLUMNS on CONSTRAINT_NAME and OWNER returns the exact column or columns involved, in their defined position for multi-column keys. Keep the two queries separate: an inventory query that tries to also return column lists produces one row per column instead of one row per constraint, which breaks the clean per-table grouping this query is built for.
Run it before any schema comparison or migration
Before comparing constraint coverage between a source and target schema — a common pre-migration or pre-refresh step — running this query against both schemas and diffing the output is faster and more reliable than comparing DDL scripts by eye. Differences in constraint type, or a constraint present on one side and missing on the other, surface immediately in the type-sorted output.
Watch for orphaned foreign keys after partial exports
A Foreign Key constraint whose "IDX" column (via R_CONSTRAINT_NAME) points at a constraint name that doesn't independently appear as a P or U row elsewhere in the output for that owner is a sign the referenced parent table or key lives in a different schema, or has been dropped. This is a fast visual check worth doing on any export or clone that only copied part of a larger schema.
When to Run This Query
- Auditing a schema you've just inherited or been handed access to, where the constraint layout isn't documented anywhere.
- Comparing constraint coverage between two schemas before a migration, refresh, or environment promotion.
- Investigating an
ORA-02291(parent key not found) orORA-02292(child record found) error, to see the full set of foreign keys on the tables involved before tracing the specific violation. - Reviewing a third-party or vendor-supplied schema before granting it production access, to confirm the expected primary and unique keys actually exist.
- Confirming a bulk load or
ALTER TABLEchange didn't leave a constraint disabled, by extending the query withSTATUSandVALIDATED. - Documenting a schema's structural rules as part of a change-control or compliance review.
Troubleshooting Common Issues
ORA-00942: table or view does not exist on dba_constraints. This means the connected account lacks the privilege to see the DBA_-prefixed view — typically SELECT_CATALOG_ROLE or SELECT ANY DICTIONARY. If a DBA grant isn't available, substitute ALL_CONSTRAINTS (visible constraints the account has access to) or USER_CONSTRAINTS (constraints owned by the connected schema only) and drop the owner predicate accordingly, since USER_CONSTRAINTS has no OWNER column to filter on.
The query returns no rows even though the user clearly owns tables. WHERE owner LIKE '&user' is case-sensitive against the stored value, and Oracle stores unquoted identifiers, including usernames, in upper case by default. Entering a lower-case username at the prompt silently returns zero rows rather than an error. Enter the username in upper case, or wrap the predicate in UPPER(owner) LIKE UPPER('&user') to remove the sensitivity.
The "IDX" column is blank for a row you expected to have a value. This is expected for C, O, and V constraint types, since neither INDEX_NAME nor R_CONSTRAINT_NAME applies to a check or view constraint. It is not an error in the query.
A Foreign Key's "IDX" value doesn't look like an index name. That's by design — for R rows, the column is populated from R_CONSTRAINT_NAME, which is the name of the referenced constraint (usually a primary key), not an index on the table you queried. Look up that constraint name directly, or on the parent table's own row set, to find the index actually enforcing it.
References
- DBA_CONSTRAINTS — Oracle Database Reference 21c - Column reference for the view, including CONSTRAINT_TYPE, INDEX_NAME, R_CONSTRAINT_NAME, STATUS, and VALIDATED
- DECODE — Oracle Database SQL Language Reference 21c - Syntax reference for the DECODE function used to translate constraint type codes into readable labels
- NVL — Oracle Database SQL Language Reference 21c - Syntax reference for the NVL function used to merge INDEX_NAME and R_CONSTRAINT_NAME into one output column
- Constraints — Oracle Database SQL Language Reference 21c - The ENABLE/DISABLE and VALIDATE/NOVALIDATE clauses behind the STATUS and VALIDATED states discussed above
- ORA-02100 to ORA-02299 — Oracle Database Error Messages 21c - ORA-02291 (parent key not found) and ORA-02292 (child record found), the two foreign-key errors this query is run to investigate
Posts in this series
- Oracle: List Tables Using a Table as a Foreign Key
- dba_constraints: Query All Oracle Table Constraints
- Oracle: Enable Disabled Foreign Key Constraints
- Oracle Disabled Foreign Key Constraints: dba_constraints
- ORA-02298: Find Missing Foreign Key Values in Oracle
- Oracle: Find Parent Tables for Repair Using dba_constraints
- Oracle User Table Constraints Query with dba_constraints