Skip to content

Supertype and Subtype Schemas ​

Data models often need to handle entities that share common traits but also have highly specific details.

A common example is a retail bank. A bank offers dozens of account types, from savings accounts to mortgages and credit cards. All are accounts, but the data they track barely overlaps. A mortgage tracks the property value and remaining principal. A savings account tracks deposits and withdrawals. A credit card tracks the credit limit and minimum payment due.

The problem: the consolidated mega-fact table ​

The most obvious approach is creating one fact table containing every possible measure for a given reporting period, such as a daily snapshot_date.

account_typesnapshot_datebalancedeposits_madeproperty_valueminimum_paymentcredit_limit
SavingsJan 1$5,0003NULLNULLNULL
MortgageJan 1-$300,000NULL$400,000$1,500NULL
Credit CardJan 1-$2,000NULLNULL$50$20,000

Across dozens of products, this creates hundreds of columns. Most of these will be NULL for any given row. Users cannot tell which columns apply to which products. Also, every query needs an account_type filter so it does not combine unrelated measures.

The solution: core and custom schemas ​

The solution is the supertype and subtype design pattern. You split the model in two. A supertype (core) schema holds the data every entity shares. A subtype (custom) schema holds exactly what each specific entity needs.

Core schema ​

The core fact table only includes measures that exist for every single entity without exception.

Core Account Snapshot Fact:

snapshot_dateaccount_keyaccount_type_keybalance
Jan 1101Savings$5,000
Jan 1102Mortgage-$300,000
Jan 1103Credit Card-$2,000

Core Account Dimension:

account_keycustomer_nameopen_datestatus
101John DoeJan 2025Active
102Mary SmithFeb 2025Active
103John DoeMar 2025Active

This answers broad questions across the entire business, like total balances, customers holding multiple product types, and portfolio growth over time.

NOTE

A measure shared by only some products belongs in the custom schemas. For example, interest_rate applies to mortgages and credit cards, but not savings accounts, so it stays out of the core. Keeping the core narrow makes it usable. If the core starts collecting measures that are only somewhat common, it becomes the messy mega-fact table again.

Custom schemas ​

Each product gets its own fact table containing all the measures relevant to it, including the shared ones.

Savings Account Fact:

snapshot_dateaccount_keybalancedeposits_madewithdrawals_made
Jan 1101$5,00031

Mortgage Fact:

snapshot_dateaccount_keyloan_balanceproperty_valueminimum_paymentprincipal_paidinterest_rate
Jan 1102-$300,000$400,000$1,500$100,0007%

Credit Card Fact:

snapshot_dateaccount_keybalancecredit_limitminimum_paymentcash_advanceinterest_rate
Jan 1103-$2,000$20,000$50$03%

Repeating balance in the custom tables is deliberate. It means a mortgage report never has to join back to the core fact table to get that number. Joining two fact tables together is slow and easy to mess up. The core duplicates a few common measures so you do not have to combine every single custom table to answer broad questions.

WARNING

balance is signed from the bank's perspective: deposit products positive, credit products negative. A savings balance of $5,000 is money the bank holds; a mortgage balance of -$300,000 is money owed to the bank. This is what makes SUM(balance) across products meaningful - it returns net customer position.

Custom schemas can also have their own dimensions. For example, a mortgage table might link to a property dimension that no other product uses.

The same account_key appears in both schemas, so every custom fact table joins directly to the core account dimension. This means a mortgage report can group by customer name or filter on account status without needing to duplicate those details.

The account_type_key is the path going the other way. A user looking at a core-level total can see which product types make up that total. This tells them which custom schema they need to open for more details.