Question presented to candidate: "We sell laptops, shirts and headphones, and every category has different attributes that product managers keep adding. How would you model those flexible attributes in PostgreSQL? Compare typed columns, an EAV table and JSONB, and tell me how each one performs when we filter on them."
What a strong answer should cover:
- The three models: typed columns (per category table or nullable columns), entity-attribute-value rows, and a
jsonbdocument per row, plus the hybrid: real columns for core, hot and constrained fields,jsonbfor the long tail - EAV costs: one row per attribute (3x the rows and about 3.5x the storage in the demo), a pivot or self-join per filter, values stored as text so comparisons and constraints break
- JSONB costs: no per-key statistics (the planner guessed 100 rows where 4000 matched), no foreign keys or types per key unless you add CHECK constraints, and whole-document rewrites on update
- Fixes that keep JSONB fast: expression indexes (which also give the planner statistics), GIN for containment, generated columns to promote a hot key, CHECK constraints for required types
- A decision rule: promote any attribute you filter, sort, join or constrain on to a real column; keep display-only and rarely queried attributes in JSONB; avoid EAV except for truly user-defined fields
Clarifying questions expected:
- Which attributes are filtered or sorted on, and how often, versus only displayed?
- Who defines new attributes: engineers in a migration, or end users at runtime?
- Do attributes need types, required-ness or references to other tables?
Code / implementation expected: Yes — the same filter on EAV and JSONB, and a hybrid PostgreSQL table with a generated column, a CHECK constraint and evidence from the planner.
Schema used by the examples: