October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

PostgreSQL Polymorphic Associations: Choose Between a Type/ID Pair and Per-Table Foreign Keys

A PostgreSQL foreign key targets one table. Learn when to use per-type foreign keys with an exactly-one check, and when a type/ID pair is worth application-owned integrity.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A PostgreSQL foreign key references one table; it cannot use a type value to choose among several parent tables. For a small, stable set of parent types, use one nullable foreign key per type and a CHECK constraint requiring exactly one parent. For an intentionally open-ended set, a commentable_type/commentable_id pair can be more extensible, but the application must take responsibility for verifying references and handling deletions.

Why one ID cannot reference several tables

A conventional foreign key names a specific referenced table and columns. Its target must be backed by a primary key, unique constraint, or qualifying unique index, and the referencing and referenced columns must have compatible types and counts. A commentable_id value alone cannot tell PostgreSQL whether to look in posts, photos, or another table. [PostgreSQL 18: Constraints]

As an Amazon Associate I earn from qualifying purchases.

That is the key distinction: a type/ID pair can describe a polymorphic relationship in application data, but it is not a foreign key covering all the possible target tables. PostgreSQL cannot use an ordinary foreign-key constraint to reject an ID that does not exist in the table selected by the type value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose based on how much integrity and flexibility you need

Pattern Can the database verify the parent exists? Adding a parent type Main trade-off
One type/ID pair No, not with an ordinary foreign key when targets vary by type Usually does not require a new child-table column, but application resolution must support the new type Extensible representation; validation, deletion handling, and orphan cleanup belong to application logic or another explicitly designed mechanism
One nullable foreign key per type plus an exactly-one check Yes; each foreign key checks its own target table Requires a schema change and corresponding query/application updates Strong direct references for a fixed set, with more columns as supported types grow
Shared parent registry A child can reference the common registry row Can support more kinds behind one common identity Adds a registry row and lifecycle relationship; subtype and registry alignment must be maintained
Separate child or association table per parent type Yes, through each table’s direct foreign key Requires another association or child structure Explicit references, with shared fields or cross-type reads potentially needing duplication or a union/view

The foreign-key behaviors in this comparison follow PostgreSQL’s constraint model. The registry and separate-table patterns are schema-design options built around those ordinary foreign keys, not automatic polymorphic features. [PostgreSQL 18: Constraints]

For a small fixed set, use an exclusive arc

An exclusive arc stores one nullable FK column for each supported parent type and adds a row-local check that exactly one is populated. Each REFERENCES clause verifies that its named parent exists; the check prevents a child from naming no parent or multiple parents.

CREATE TABLE comments (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  post_id bigint REFERENCES posts(id) ON DELETE CASCADE,
  photo_id bigint REFERENCES photos(id) ON DELETE CASCADE,
  body text NOT NULL,
  CONSTRAINT comments_exactly_one_parent
    CHECK (num_nonnulls(post_id, photo_id) = 1)
);

The ON DELETE CASCADE clauses above are illustrative, not a universal policy. Choose the action that matches the child record’s meaning:

  • CASCADE: delete the child rows when their parent is deleted.
  • RESTRICT or NO ACTION: prevent a parent deletion that would leave referencing rows, subject to the timing and constraints involved. NO ACTION is the default.
  • SET NULL: clear the referencing column when the parent is deleted. With an exactly-one check, that can make the child row invalid, so the check and child lifecycle policy must be designed together.

PostgreSQL documents these FK actions and notes that they remain subject to other constraints on the table. A check constraint evaluates the row being checked; it is not a way to look up a parent in another table. [PostgreSQL 18: Constraints] [PostgreSQL 16: Constraints]

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Index the referencing columns when the workload calls for it

PostgreSQL requires an appropriate unique target on the referenced side, but it does not automatically create an index on the referencing FK columns. Index columns such as post_id or photo_id when lookups or parent updates and deletes make them useful; the right indexes depend on access patterns. [PostgreSQL 18: Constraints]

Be deliberate with nulls and composite keys

A check constraint passes when its expression evaluates to true or null, so express the exactly-one rule explicitly or use appropriate NOT NULL constraints where they fit. For a composite foreign key, PostgreSQL’s default MATCH SIMPLE allows the constraint to be bypassed if any referencing component is null. MATCH FULL instead requires all components to be null or all to match. [PostgreSQL 16: Constraints] [PostgreSQL 18: Constraints]

Use a type/ID pair only with an explicit integrity owner

A pair such as commentable_type and commentable_id stores a discriminator and an ID. The application reads the discriminator, selects the corresponding table, and resolves the ID. This is a reasonable trade-off when parent types are genuinely open-ended or change frequently and schema changes for each new type are undesirable.

The cost is that PostgreSQL’s ordinary FK constraint cannot verify that the chosen parent exists. A deletion can leave orphaned child rows unless the application, a trigger strategy, or another explicit process handles it. Likewise, a CHECK constraint cannot make this a cross-table existence check: PostgreSQL does not support checks that reliably depend on other rows or tables. Assign a clear owner for validation, concurrent changes, deletion behavior, orphan detection, and cleanup. [PostgreSQL 16: Constraints]

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Consider a registry or separate association tables when the model fits

Shared parent registry

A registry table such as commentables can provide a common identity. Comments reference that table through one FK, while subtype records share the registry key. This preserves a database-enforced reference to a registry row, but it adds a row and lifecycle relationship. The design still needs to ensure that each registry entry corresponds to the intended subtype and that creation and deletion keep them aligned.

One association table per parent type

Tables such as post_comments and photo_comments can each declare a direct FK to their respective parent. This keeps references explicit. If the child fields are shared, separate structures can duplicate them; if the application needs a unified view across types, it may need a union or view layer.

Do not rely on inheritance to supply missing foreign keys

PostgreSQL inheritance can make queries against a parent table include descendant rows by default, but primary-key, unique, and foreign-key constraints are not inherited by child tables. Inheritance therefore does not automatically turn separate descendant tables into one FK target for a polymorphic association. [PostgreSQL 17: Inheritance]

Practical decision

  • Choose per-type nullable FKs plus an exactly-one check when the parent types are known and stable and PostgreSQL should enforce parent existence and deletion rules.
  • Choose a type/ID pair when the parent set is intentionally open-ended and the team accepts responsibility for application-level validation and cleanup.
  • Choose a shared registry when many parent kinds need one stable identity and the extra registry/subtype lifecycle is acceptable.
  • Choose separate association tables when explicit direct FKs matter more than a single shared child table.

These patterns differ in constraints and schema maintenance; the documented behavior does not establish a universal performance winner. Benchmark a representative workload if query speed is part of the decision.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.