{"id":25730,"date":"2026-09-02T14:07:40","date_gmt":"2026-09-02T20:07:40","guid":{"rendered":"https:\/\/www.designandexecute.com\/designs\/?p=25730"},"modified":"2026-09-05T04:00:51","modified_gmt":"2026-09-05T10:00:51","slug":"interview-questions-for-data-modeling","status":"publish","type":"post","link":"https:\/\/www.designandexecute.com\/designs\/interview-questions-for-data-modeling\/","title":{"rendered":"Interview Questions for Data Modeling"},"content":{"rendered":"\n<p>Here is a comprehensive list of technical data modeling interview questions, categorized by skill level. These questions focus on <strong>relational<\/strong> (OLTP) and <strong>dimensional<\/strong> (OLAP\/Data Warehousing) modeling, with a touch of <strong>NoSQL<\/strong> for the advanced tier.<\/p>\n\n\n\n<p>I have also included <strong>&#8220;Expected Answer Bullets&#8221;<\/strong> so you know exactly what to listen for.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Beginner Level (Conceptual &amp; Logical Understanding)<\/h3>\n\n\n\n<p><em>Goal: Ensure they understand the basics of keys, normalization, and relationship types.<\/em><\/p>\n\n\n\n<p><strong>1. The &#8220;Employee Manager&#8221; Problem (Relationships)<\/strong><br><em>Question:<\/em> In an <code>Employees<\/code> table, how would you model the relationship where one employee manages another? What is this type of relationship called?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong> It is a <strong>Recursive (or Self-Referencing) Foreign Key<\/strong>. The table would have an <code>EmployeeID<\/code> (Primary Key) and a <code>ManagerID<\/code> (Foreign Key that references <code>EmployeeID<\/code>). This represents a one-to-many relationship (one manager has many employees).<\/li>\n<\/ul>\n\n\n\n<p><strong>2. Normalization Explainers<\/strong><br><em>Question:<\/em> Briefly explain 1st, 2nd, and 3rd Normal Form (1NF, 2NF, 3NF) in plain English.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>1NF:<\/strong> Atomic values (no repeating groups or arrays in a single column).<\/li>\n\n\n\n<li><strong>2NF:<\/strong> Must be in 1NF, and every non-key column must depend on the <strong>entire<\/strong> primary key (relevant for composite keys).<\/li>\n\n\n\n<li><strong>3NF:<\/strong> Must be in 2NF, and no non-key column depends on another non-key column (no transitive dependencies).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>3. Natural vs. Surrogate Keys<\/strong><br><em>Question:<\/em> What is the difference between a natural key and a surrogate key? Give an example of when you would use each.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Natural Key:<\/strong> A column that already exists in the real world (e.g., <code>SSN<\/code>, <code>ISBN<\/code>, <code>VIN<\/code>). Risky because they can change or be reused.<\/li>\n\n\n\n<li><strong>Surrogate Key:<\/strong> An artificially generated unique identifier (e.g., an <code>INT IDENTITY<\/code> or <code>UUID<\/code>). Preferred in data warehouses because it isolates the fact table from changes in the source system.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Intermediate Level (Physical Design &amp; Dimensional Modeling)<\/h3>\n\n\n\n<p><em>Goal: Assess their ability to handle performance, historical data, and warehousing structures.<\/em><\/p>\n\n\n\n<p><strong>4. Indexing Strategy<\/strong><br><em>Question:<\/em> You have a <code>Sales<\/code> table with 1 billion rows. Users frequently query by <code>Order_Date<\/code> and <code>Customer_ID<\/code>. How would you index this table, and what is the danger of creating too many indexes?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Create a <strong>Composite Non-Clustered Index<\/strong> on (<code>Customer_ID<\/code>, <code>Order_Date<\/code>) because <code>Customer_ID<\/code> is likely highly selective.<\/li>\n\n\n\n<li><strong>Danger:<\/strong> Too many indexes drastically slow down <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> operations (write amplification) because the index must be updated alongside the table.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>5. Star Schema vs. Snowflake Schema<\/strong><br><em>Question:<\/em> When building a data warehouse, when would you choose a Star Schema over a Snowflake Schema?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Star Schema:<\/strong> Denormalized dimensions. Preferred for simplicity, faster query performance (fewer joins), and easier use by BI tools.<\/li>\n\n\n\n<li><strong>Snowflake Schema:<\/strong> Normalized dimensions. Used to save storage space or when dimensions are extremely complex. <strong>Downside:<\/strong> More joins = slower queries.<\/li>\n\n\n\n<li><em>Key insight to listen for:<\/em> &#8220;Always start with Star and only snowflake if storage is a crisis.&#8221;<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>6. Handling SCD Type 2<\/strong><br><em>Question:<\/em> A customer changes their address. In a data warehouse, we need to keep the historical address for old orders. How do you model this? (Hint: SCD)<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong> This is a <strong>Slowly Changing Dimension (SCD) Type 2<\/strong>.\n<ul class=\"wp-block-list\">\n<li>Add columns: <code>Start_Date<\/code>, <code>End_Date<\/code> (or <code>NULL<\/code> for current), and <code>Is_Current_Flag<\/code>.<\/li>\n\n\n\n<li>Instead of updating the old record, you <strong>expire<\/strong> the old row (set <code>End_Date<\/code> = yesterday) and <strong>insert<\/strong> a new row with the new address, a new surrogate key, and <code>Start_Date<\/code> = today. This ensures historical fact tables still point to the old surrogate key.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Advanced Level (Scalability, NoSQL, &amp; Complex Optimization)<\/h3>\n\n\n\n<p><em>Goal: Stress-test their critical thinking on distributed systems, anti-patterns, and enterprise architecture.<\/em><\/p>\n\n\n\n<p><strong>7. The Polymorphic Association (Anti-Pattern)<\/strong><br><em>Question:<\/em> You have an <code>Orders<\/code> table and a <code>Returns<\/code> table. You are asked to create a single <code>Notes<\/code> table that can attach comments to <em>either<\/em> an Order or a Return. The junior developer proposes a table with columns: <code>NoteID<\/code>, <code>Note_Text<\/code>, <code>Parent_Type<\/code> (&#8216;Order&#8217; or &#8216;Return&#8217;), and <code>Parent_ID<\/code>. Why is this a bad practice, and how would you fix it?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Why it&#8217;s bad:<\/strong> You cannot enforce a Foreign Key constraint because the <code>Parent_ID<\/code> references two different tables. This breaks referential integrity.<\/li>\n\n\n\n<li><strong>Fix:<\/strong> Create <strong>two separate junction tables<\/strong>: <code>Order_Notes<\/code> and <code>Return_Notes<\/code>. Or, use <strong>concrete super-types<\/strong> (create a generic <code>Event<\/code> table that both Orders and Returns inherit from, then link Notes to <code>Event_ID<\/code>).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>8. Eventual Consistency vs. Strong Consistency<\/strong><br><em>Question:<\/em> You are modeling a microservices architecture. You have a <code>Product Catalog<\/code> service and an <code>Inventory<\/code> service. How do you model the data relationship between these two services if they use separate databases, and what consistency model do you choose?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Modeling:<\/strong> There is <strong>no foreign key<\/strong> across databases. You store the <code>Product_ID<\/code> (as a value) in the Inventory service, but do not enforce a physical relationship.<\/li>\n\n\n\n<li><strong>Consistency:<\/strong> You must use <strong>Eventual Consistency<\/strong> via asynchronous messaging (e.g., Kafka, SQS). If a product is deleted in Catalog, an event is published to update Inventory. You cannot use a distributed transaction (2PC) because it creates tight coupling and availability issues.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>9. Query Optimization using Execution Plans<\/strong><br><em>Question:<\/em> You run a query that joins a 10-million-row <code>Fact_Sales<\/code> table to a 10,000-row <code>Dim_Product<\/code> table. The query takes 45 seconds. You look at the execution plan and see a &#8220;Hash Match&#8221; join and a &#8220;Key Lookup&#8221; (Clustered) taking 80% of the cost. How do you fix this?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Problem:<\/strong> The &#8220;Key Lookup&#8221; means the index used does not &#8220;cover&#8221; the query. The database is finding the rows in the index, then jumping back to the clustered table to get the remaining columns.<\/li>\n\n\n\n<li><strong>Fix 1:<\/strong> Create a <strong>Covering Non-Clustered Index<\/strong> on <code>Dim_Product<\/code> that includes all the columns requested in the <code>SELECT<\/code> and <code>WHERE<\/code> clauses (or use <code>INCLUDE<\/code> for the heavy text columns).<\/li>\n\n\n\n<li><strong>Fix 2:<\/strong> If filtering heavily, change the join order using <code>FORCE ORDER<\/code> or rewrite the query to push filters into a subquery before the join (reduce the Hash Match bucket size).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>10. Designing for Time-Series Data (Partitioning)<\/strong><br><em>Question:<\/em> You are modeling a table for IoT sensor data that will ingest 1 million rows per hour. Retention policy requires keeping data online for 3 years. What physical database design technique is mandatory for this scenario, and how does it impact your key design?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Expected Answer:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Technique:<\/strong> <strong>Range Partitioning<\/strong> (usually by date). You should partition the table by the <code>Event_Time<\/code> column (e.g., daily or monthly partitions).<\/li>\n\n\n\n<li><strong>Key Design:<\/strong> The Primary Key <strong>must<\/strong> include the partition key (e.g., <code>PRIMARY KEY (Sensor_ID, Event_Time)<\/code>). This creates a composite key that ensures uniqueness within a partition.<\/li>\n\n\n\n<li><strong>Bonus Insight:<\/strong> This allows for <strong>Partition Switching<\/strong> (fast loading of new data) and <strong>Partition Truncating<\/strong> (instant deletion of old data without locking the table).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>11. The Polymorphic Contact Manager (Anti-Pattern Identification)<\/strong><\/p>\n\n\n\n<p><em>Question:<\/em> You are reviewing a legacy database design. The team has a&nbsp;<code>Person<\/code>&nbsp;table with a&nbsp;<code>PersonID<\/code>. They also have a&nbsp;<code>ContactManager<\/code>&nbsp;table with the following columns:&nbsp;<code>ContactManagerID<\/code>&nbsp;(PK),&nbsp;<code>PersonID<\/code>&nbsp;(FK to Person),&nbsp;<code>ContactID<\/code>&nbsp;(INT), and&nbsp;<code>ContactType<\/code>&nbsp;(VARCHAR check constraint: &#8216;Email&#8217;, &#8216;Phone&#8217;, &#8216;Address&#8217;).<\/p>\n\n\n\n<p>The logic is: if&nbsp;<code>ContactType<\/code>&nbsp;= &#8216;Email&#8217;, the&nbsp;<code>ContactID<\/code>&nbsp;references the&nbsp;<code>Email<\/code>&nbsp;table; if &#8216;Phone&#8217;, it references the&nbsp;<code>Phone<\/code>&nbsp;table; if &#8216;Address&#8217;, it references the&nbsp;<code>Address<\/code>&nbsp;table.<\/p>\n\n\n\n<p>What is this design pattern called? Is this an ideal approach for a relational database? If not, explain the specific technical flaws and provide two alternative solutions to fix this design.<\/p>\n\n\n\n<p><strong>Expected Answer:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Beginner Level (Awareness):<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Pattern Name:<\/strong>&nbsp;&#8220;Polymorphic Associations&#8221; or &#8220;Generic Foreign Keys.&#8221;<\/li>\n\n\n\n<li><strong>Initial Assessment:<\/strong>&nbsp;&#8220;I don&#8217;t think this is a good idea, because I can&#8217;t draw a proper ER diagram for it. It feels like the database can&#8217;t enforce rules properly.&#8221;<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Intermediate Level (Flaw Identification):<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Is it ideal?<\/strong>&nbsp;No, this is considered an anti-pattern in relational modeling.<\/li>\n\n\n\n<li><strong>Specific Technical Flaws:<\/strong>\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>No Referential Integrity:<\/strong>&nbsp;You cannot define a Foreign Key constraint on&nbsp;<code>ContactID<\/code>&nbsp;because it points to three different tables based on the&nbsp;<code>ContactType<\/code>. The database cannot guarantee that&nbsp;<code>ContactID<\/code>&nbsp;123 actually exists in the&nbsp;<code>Email<\/code>&nbsp;table when&nbsp;<code>ContactType<\/code>&nbsp;= &#8216;Email&#8217;.<\/li>\n\n\n\n<li><strong>Complex Querying:<\/strong>&nbsp;To retrieve a person&#8217;s contact info, you must write complex&nbsp;<code>CASE<\/code>&nbsp;statements,&nbsp;<code>UNION<\/code>&nbsp;queries, or join all three tables and&nbsp;<code>COALESCE<\/code>&nbsp;the results. You cannot use simple&nbsp;<code>INNER JOIN<\/code>s.<\/li>\n\n\n\n<li><strong>Indexing Inefficiency:<\/strong>&nbsp;A single index on&nbsp;<code>ContactID<\/code>&nbsp;is inefficient because the ID values across the three parent tables are semantically different and may overlap.<\/li>\n\n\n\n<li><strong>Constraint Limitations:<\/strong>&nbsp;You cannot enforce type-specific rules (e.g., requiring an&nbsp;<code>Email_Address<\/code>&nbsp;to be&nbsp;<code>NOT NULL<\/code>&nbsp;or a&nbsp;<code>Phone_Number<\/code>&nbsp;to have a&nbsp;<code>Country_Code<\/code>) because the&nbsp;<code>ContactManager<\/code>&nbsp;table only holds generic IDs.<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Advanced Level (Solutions &amp; Trade-offs):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;To fix this, I would propose two solutions depending on the use case:&#8221;<\/em><\/li>\n\n\n\n<li><strong>Solution A: Supertype\/Subtype (Best for Transactional\/OLTP systems)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Create a master&nbsp;<code>Contacts<\/code>&nbsp;table with a&nbsp;<code>ContactID<\/code>&nbsp;(PK) and&nbsp;<code>ContactType<\/code>.<\/li>\n\n\n\n<li>Change the&nbsp;<code>Person<\/code>&nbsp;table to have an FK directly referencing this new&nbsp;<code>Contacts.ContactID<\/code>.<\/li>\n\n\n\n<li>Change the&nbsp;<code>Email<\/code>,&nbsp;<code>Phone<\/code>, and&nbsp;<code>Address<\/code>&nbsp;tables so their Primary Key is&nbsp;<em>not<\/em>&nbsp;auto-generated. Instead, their&nbsp;<code>ContactID<\/code>&nbsp;acts as both the Primary Key and a Foreign Key back to the master&nbsp;<code>Contacts<\/code>&nbsp;table. This enforces that every subtype record has a valid master record, and maintains a hard FK between Person and Contact.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Solution B: Multiple Explicit Foreign Keys (Simpler, but less extensible)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Remove the&nbsp;<code>ContactManager<\/code>&nbsp;table entirely.<\/li>\n\n\n\n<li>Add three nullable columns directly to the&nbsp;<code>Person<\/code>&nbsp;table:&nbsp;<code>EmailID<\/code>&nbsp;(FK to Email),&nbsp;<code>PhoneID<\/code>&nbsp;(FK to Phone), and&nbsp;<code>AddressID<\/code>&nbsp;(FK to Address).<\/li>\n\n\n\n<li>This allows the database to enforce strict referential integrity for each type individually.<\/li>\n\n\n\n<li><em>Drawback:<\/em>&nbsp;Requires schema changes (new columns) every time a new contact type (e.g.,&nbsp;<code>SocialMediaID<\/code>) is introduced.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Expert Level (Contextual Awareness):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;Before choosing a fix, I need to clarify the business requirement. If this is a Data Warehouse (OLAP), I wouldn&#8217;t use either of those relational solutions. I would likely denormalize the data and store the actual Email, Phone, and Address values directly in a wide&nbsp;<code>Dim_Person<\/code>&nbsp;table (with NULLs), because we don&#8217;t enforce Foreign Keys in dimensional models, and we prioritize query performance over storage normalization.&#8221;<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Is solution A still polymorphic?<\/li>\n\n\n\n<li><strong>Follow-up Question (Optional):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;In Solution A, we introduced a master&nbsp;<code>Contacts<\/code>&nbsp;table. Is this still considered a polymorphic association? Why or why not?&#8221;<\/em><br><strong>Expected Answer:<\/strong><br><strong>Beginner:<\/strong>&nbsp;&#8220;I think it is, because we still have a&nbsp;<code>ContactType<\/code>&nbsp;column.&#8221;<br><strong>Intermediate:<\/strong>&nbsp;&#8220;No, it is not polymorphic anymore. The&nbsp;<code>ContactID<\/code>&nbsp;in the&nbsp;<code>Person<\/code>&nbsp;table now references only one table (<code>Contacts<\/code>), so we can enforce a proper Foreign Key constraint. The&nbsp;<code>ContactType<\/code>&nbsp;is just an attribute now.&#8221;<br><strong>Advanced (The &#8220;Aha!&#8221; insight):<\/strong><br><em>&#8220;It is no longer polymorphic because we have removed the ambiguity. In the original design, the&nbsp;<code>ContactID<\/code>&nbsp;meant three different things depending on the row, that was the polymorphism. In Solution A, the&nbsp;<code>ContactID<\/code>&nbsp;always means &#8216;a row in the Contacts table.&#8217; The type column simply tells you which child table to join to for the details. This is standard relational inheritance modeling, often called&nbsp;<strong>Single Table Inheritance<\/strong>&nbsp;at the physical level, but strictly speaking, it is a&nbsp;<strong>Supertyping<\/strong>&nbsp;pattern, not a polymorphic association.&#8221;<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Pro Tip for your interview:<\/strong>&nbsp;The best senior candidates won&#8217;t just stop at fixing the FK, they will ask you&nbsp;<em>&#8220;What queries run against this?&#8221;<\/em>&nbsp;because if you rarely query contacts across all types, Solution B (3 nullable FKs) might actually be faster and simpler than Solution A (which requires an extra join to the master table for every contact lookup). The expert knows there is no &#8220;perfect&#8221; solution, only trade-offs based on access patterns.<\/li>\n<\/ul>\n\n\n\n<p><strong>12. Handling Shared Contacts (Many-to-Many Scenario)<\/strong><\/p>\n\n\n\n<p><em>Question:<\/em> Building on the previous Supertype\/Subtype model (Solution A), we now have a new business requirement:&nbsp;<strong>An Address can be shared by multiple People<\/strong>&nbsp;(e.g., family members living at the same residence, or a business address used by many employees).<\/p>\n\n\n\n<p>However, an Email and a Phone number are still considered&nbsp;<strong>unique to a single Person<\/strong>&nbsp;(cannot be shared).<\/p>\n\n\n\n<p>Given this mixed requirement, how would the Supertype\/Subtype model need to be altered? Walk through the specific structural changes and explain why this is now a different relationship pattern.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>Expected Answer:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Beginner Level (Awareness):<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;I would just put the same&nbsp;<code>ContactID<\/code>&nbsp;into multiple&nbsp;<code>Person<\/code>&nbsp;records. So, if two people share an address, they both have the same&nbsp;<code>AddressID<\/code>&nbsp;in their&nbsp;<code>Person<\/code>&nbsp;row.&#8221;<\/li>\n\n\n\n<li><em>Interviewer note:<\/em>&nbsp;This shows they understand the concept but haven&#8217;t yet considered the data integrity or modeling implications. It also breaks the uniqueness assumption of the&nbsp;<code>Contacts<\/code>&nbsp;table if we use the original foreign key approach.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Intermediate Level (Structural Understanding):<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>The Problem with the Original Model:<\/strong>\n<ul class=\"wp-block-list\">\n<li>In Solution A,&nbsp;<code>Person<\/code>&nbsp;has a&nbsp;<strong>one-to-many<\/strong>&nbsp;relationship with&nbsp;<code>Contacts<\/code>&nbsp;(one Person, many Contacts).<\/li>\n\n\n\n<li>If an Address is shared, we now have a&nbsp;<strong>many-to-many<\/strong>&nbsp;relationship between&nbsp;<code>Person<\/code>&nbsp;and&nbsp;<code>Contacts<\/code>&nbsp;for&nbsp;<em>that specific ContactType<\/em>.<\/li>\n\n\n\n<li>We cannot simply put the same&nbsp;<code>ContactID<\/code>&nbsp;in multiple&nbsp;<code>Person<\/code>&nbsp;rows unless we change the relationship and remove the FK constraint from&nbsp;<code>Person<\/code>, which breaks referential integrity.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>The Fix:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Remove the&nbsp;<code>PersonID<\/code>&nbsp;column from the&nbsp;<code>Contacts<\/code>&nbsp;master table.<\/li>\n\n\n\n<li>Create a&nbsp;<strong>junction (bridge) table<\/strong>&nbsp;called&nbsp;<code>PersonContact<\/code>&nbsp;with a composite key of (<code>PersonID<\/code>,&nbsp;<code>ContactID<\/code>).<\/li>\n\n\n\n<li><code>PersonID<\/code>&nbsp;is an FK to&nbsp;<code>Person<\/code>.<\/li>\n\n\n\n<li><code>ContactID<\/code>&nbsp;is an FK to&nbsp;<code>Contacts<\/code>.<\/li>\n\n\n\n<li>This allows any Person to link to any Contact, and any Contact to link to multiple People.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Advanced Level (Constraints &amp; Business Logic Enforcement):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;The structural fix is a junction table, but that alone isn&#8217;t enough\u2014I need to enforce the business rules at the database level to prevent emails and phones from being shared, while allowing addresses to be shared.&#8221;<\/em><\/li>\n\n\n\n<li><strong>How to enforce this:<\/strong>\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Option A (Database Constraints):<\/strong>&nbsp;Add a&nbsp;<code>Is_Shared<\/code>&nbsp;boolean column to the&nbsp;<code>Contacts<\/code>&nbsp;master table. Then create a&nbsp;<strong>unique constraint<\/strong>&nbsp;on (<code>ContactID<\/code>,&nbsp;<code>PersonID<\/code>) in the junction table\u2014but that doesn&#8217;t solve the sharing restriction.<\/li>\n\n\n\n<li><strong>Option B (Trigger or Application Logic &#8211; Practical approach):<\/strong>&nbsp;The junction table itself doesn&#8217;t prevent sharing. To enforce the rule, you would:\n<ul class=\"wp-block-list\">\n<li>Allow multiple&nbsp;<code>PersonContact<\/code>&nbsp;rows for the same&nbsp;<code>ContactID<\/code>.<\/li>\n\n\n\n<li><strong>But<\/strong>, add a&nbsp;<code>Max_Shared<\/code>&nbsp;or&nbsp;<code>Is_Shareable<\/code>&nbsp;column to the&nbsp;<code>ContactType<\/code>&nbsp;lookup table (or directly on&nbsp;<code>Contacts<\/code>).<\/li>\n\n\n\n<li>Then, use a&nbsp;<strong>trigger<\/strong>&nbsp;(or application-level validation) that checks:&nbsp;<em>&#8220;If&nbsp;<code>ContactType<\/code>&nbsp;= &#8216;Email&#8217; or &#8216;Phone&#8217;, count the number of rows in&nbsp;<code>PersonContact<\/code>&nbsp;for this&nbsp;<code>ContactID<\/code>&nbsp;and throw an error if &gt; 1.&#8221;<\/em><\/li>\n\n\n\n<li><em>Alternative:<\/em>&nbsp;If using a modern RDBMS (like PostgreSQL), use a&nbsp;<strong>partial unique index<\/strong>:sqlCREATE UNIQUE INDEX idx_unique_person_contact ON PersonContact (ContactID) WHERE ContactType IN (&#8216;Email&#8217;, &#8216;Phone&#8217;);<em>(Note: This requires the&nbsp;<code>ContactType<\/code>&nbsp;to be denormalized into&nbsp;<code>PersonContact<\/code>&nbsp;or a join in the index, which may not be supported by all databases.)<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Option C (Schema Separation &#8211; Cleanest but more tables):<\/strong>\n<ul class=\"wp-block-list\">\n<li>Keep the many-to-many junction for&nbsp;<code>Addresses<\/code>&nbsp;only:&nbsp;<code>PersonAddress<\/code>&nbsp;(<code>PersonID<\/code>,&nbsp;<code>AddressID<\/code>).<\/li>\n\n\n\n<li>Keep the one-to-many FKs for&nbsp;<code>Email<\/code>&nbsp;and&nbsp;<code>Phone<\/code>&nbsp;directly on the&nbsp;<code>Person<\/code>&nbsp;table (or via a non-shared junction).<\/li>\n\n\n\n<li>This physically enforces the rule without triggers, because the junction table allows sharing, while the direct FK does not.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Expert Level (Contextual Trade-offs):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;Before implementing Option B or C, I need to ask:&nbsp;<strong>How often do addresses change?<\/strong>&nbsp;If addresses are frequently updated and shared by hundreds of employees, I might consider a&nbsp;<strong>normalized approach<\/strong>&nbsp;(Option C) to save storage and maintain single points of update. However, if this is a Data Warehouse, I&#8217;d ignore the normalization entirely and denormalize the shared address into each Person&#8217;s row to avoid complex joins in BI reports. The &#8216;ideal&#8217; model depends entirely on whether this is an OLTP system with strict write integrity or an OLAP system with read performance priorities.&#8221;<\/em><\/li>\n\n\n\n<li><em>&#8220;Additionally, I would ask: If an address is shared, and one person updates it, should it update for everyone? That dictates whether we use a &#8216;shared reference&#8217; (junction table) or copy the address value directly into each person&#8217;s row.&#8221;<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Intermediate Level (Relationship Evolution &amp; Role-Based Modeling)<\/h3>\n\n\n\n<p><strong>1B. The Employee Manager Problem \u2013 Extended (Multiple Manager Types)<\/strong><\/p>\n\n\n\n<p><em>Question:<\/em><br>Building on the previous &#8220;Employee Manager&#8221; model, the business has introduced a new requirement:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>&#8220;Each employee now has three types of managers:<\/em><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Direct Manager<\/strong>&nbsp;(the person they report to daily \u2013 the original requirement).<\/li>\n\n\n\n<li><strong>Dotted-Line Manager<\/strong>&nbsp;(a secondary manager from a different department who provides project oversight).<\/li>\n\n\n\n<li><strong>Location Manager<\/strong>&nbsp;(the manager responsible for the physical office\/branch where the employee sits).&#8221;<\/li>\n<\/ol>\n<\/blockquote>\n\n\n\n<p>How would the data model need to change to accommodate these three distinct management relationships? Walk me through the trade-offs of at least two different modeling approaches.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>Expected Answer:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Beginner Level (Awareness):<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;I would just add three columns to the&nbsp;<code>Employees<\/code>&nbsp;table:&nbsp;<code>ManagerID<\/code>,&nbsp;<code>DottedLineManagerID<\/code>, and&nbsp;<code>LocationManagerID<\/code>. All three would be Foreign Keys that reference&nbsp;<code>EmployeeID<\/code>.&#8221;<\/li>\n\n\n\n<li><em>Interviewer note:<\/em>&nbsp;This works functionally but shows a lack of foresight. It hard-codes the relationship types into the schema, meaning any new manager type (e.g., &#8220;Project Manager,&#8221; &#8220;Regional Manager,&#8221; &#8220;HR Partner&#8221;) requires an ALTER TABLE to add a new column.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Intermediate Level (Normalization &amp; Flexibility):<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Problem with the 3-column approach:<\/strong>&nbsp;It&#8217;s not extensible. If we add a 4th or 5th type of manager, we have to change the table structure, which requires downtime, migration scripts, and code changes.<\/li>\n\n\n\n<li><strong>Better Approach \u2013 Junction Table with Roles:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Remove&nbsp;<code>ManagerID<\/code>&nbsp;columns from the&nbsp;<code>Employees<\/code>&nbsp;table entirely.<\/li>\n\n\n\n<li>Create a&nbsp;<strong>junction table<\/strong>&nbsp;called&nbsp;<code>EmployeeManager<\/code>&nbsp;or&nbsp;<code>ManagementStructure<\/code>&nbsp;with the following columns:\n<ul class=\"wp-block-list\">\n<li><code>EmployeeID<\/code>&nbsp;(FK to Employees) \u2013&nbsp;<em>The subordinate.<\/em><\/li>\n\n\n\n<li><code>ManagerID<\/code>&nbsp;(FK to Employees) \u2013&nbsp;<em>The supervisor.<\/em><\/li>\n\n\n\n<li><code>RelationshipType<\/code>&nbsp;(VARCHAR or INT FK to a lookup table) \u2013 Values: &#8216;Direct&#8217;, &#8216;DottedLine&#8217;, &#8216;Location&#8217;.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>This creates a&nbsp;<strong>many-to-many relationship<\/strong>&nbsp;between Employees and themselves, with a role\/type discriminator.<\/li>\n\n\n\n<li><strong>Why this is better:<\/strong>&nbsp;Adding a new manager type (e.g., &#8220;Project Manager&#8221;) simply requires inserting a new row into a&nbsp;<code>RelationshipType<\/code>&nbsp;lookup table. No schema changes required.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Constraint Consideration:<\/strong>\n<ul class=\"wp-block-list\">\n<li>If every employee must have exactly one Direct Manager, you can enforce this with a&nbsp;<strong>unique constraint<\/strong>&nbsp;on (<code>EmployeeID<\/code>,&nbsp;<code>RelationshipType<\/code>) where&nbsp;<code>RelationshipType<\/code>&nbsp;= &#8216;Direct&#8217;, or use a filtered\/partial index. Alternatively, enforce it at the application level or via a trigger.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Advanced Level (Temporal Modeling, Hierarchy Depth, &amp; Edge Cases):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;The junction table is the right structural move, but I have several follow-up questions and enhancements:&#8221;<\/em><\/li>\n\n\n\n<li><strong>1. Temporal Tracking (Historical Auditing):<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;Does the business need to track&nbsp;<em>when<\/em>&nbsp;a management relationship changed? For example, if John had a different dotted-line manager last quarter, we might need to preserve that history for performance reviews or organizational audits.&#8221;<\/li>\n\n\n\n<li><strong>Solution:<\/strong>&nbsp;Add&nbsp;<code>Start_Date<\/code>&nbsp;and&nbsp;<code>End_Date<\/code>&nbsp;(or&nbsp;<code>Is_Current_Flag<\/code>) to the&nbsp;<code>EmployeeManager<\/code>&nbsp;junction table. This makes it an&nbsp;<strong>SCD Type 2<\/strong>&nbsp;structure for management relationships.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>2. Circular Reference Prevention:<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;We must prevent circular reporting structures (e.g., A manages B, B manages C, and C manages A), which can cause infinite loops in queries and reporting.&#8221;<\/li>\n\n\n\n<li><strong>Solution:<\/strong>&nbsp;This cannot be fully enforced by standard SQL constraints. It requires either:\n<ul class=\"wp-block-list\">\n<li><strong>Application-level validation<\/strong>&nbsp;before insert\/update.<\/li>\n\n\n\n<li>A&nbsp;<strong>trigger<\/strong>&nbsp;that checks for cycles using a recursive CTE and rolls back the transaction if a cycle is detected.<\/li>\n\n\n\n<li><strong>Database-specific features:<\/strong>&nbsp;Some databases (like PostgreSQL with&nbsp;<code>WITH RECURSIVE<\/code>) can enforce this via exclusion constraints, but typically it&#8217;s handled in the business logic layer.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>3. Depth &amp; Hierarchy Reporting:<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;How deep does the management chain go? If we need to report on the entire organizational tree (e.g., &#8216;show me all employees under VP of Engineering&#8217;), we need to consider recursive query performance.&#8221;<\/li>\n\n\n\n<li><strong>Solution:<\/strong>\n<ul class=\"wp-block-list\">\n<li>For transactional systems, use recursive CTEs to traverse the hierarchy.<\/li>\n\n\n\n<li>For reporting\/OLAP systems, consider&nbsp;<strong>denormalizing<\/strong>&nbsp;with a&nbsp;<strong>closure table<\/strong>&nbsp;(precomputing all ancestor-descendant paths) or materializing the hierarchy path (e.g., storing a&nbsp;<code>Path<\/code>&nbsp;string like&nbsp;<code>\/CEO\/VP\/Director\/Employee<\/code>).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>4. Edge Cases:<\/strong>\n<ul class=\"wp-block-list\">\n<li>&#8220;Can an employee be their own manager? (Should be prevented.)&#8221;<\/li>\n\n\n\n<li>&#8220;Can an employee have no manager (e.g., the CEO)? How do we represent NULL values in the junction table?&#8221;<\/li>\n\n\n\n<li>&#8220;What happens when a manager leaves the company? Do we cascade delete, or do we require the manager to be reassigned first?&#8221; (This shows they think about data integrity and business processes.)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Expert Level (Organizational Modeling &amp; Graph Databases):<\/strong>\n<ul class=\"wp-block-list\">\n<li><em>&#8220;If management relationships are a core domain concept with multiple types, changing over time, and we frequently query the entire organizational tree, I would consider using a&nbsp;<strong>Graph Database<\/strong>&nbsp;(like Neo4j) instead of a relational model.&#8221;<\/em><\/li>\n\n\n\n<li><em>&#8220;In a graph model, Employees are nodes, and management relationships are edges with properties (Type, Start_Date, End_Date). This makes recursive queries (&#8216;find all subordinates of X at any depth&#8217;) O(1) in traversal complexity and avoids expensive recursive CTEs in SQL.&#8221;<\/em><\/li>\n\n\n\n<li><em>&#8220;However, if we must stay in SQL, I&#8217;d evaluate whether a&nbsp;<strong>Closure Table<\/strong>&nbsp;is worth the storage overhead for faster reads, or if we can tolerate recursive CTEs for occasional queries.&#8221;<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Question<\/th><th class=\"has-text-align-left\" data-align=\"left\">Correct Answer<\/th><\/tr><\/thead><tbody><tr><td>Is this design polymorphic?<\/td><td><strong>NO.<\/strong>&nbsp;Polymorphism requires a single FK pointing to multiple tables. Here, it points to only one.<\/td><\/tr><tr><td>What is this pattern called?<\/td><td><strong>Supertype\/Subtype<\/strong>&nbsp;(or Table Inheritance).<\/td><\/tr><tr><td>What is the discriminator column for?<\/td><td><strong>Descriptive metadata<\/strong>\u2014it tells you which subtype table contains the details, but it does not route the FK.<\/td><\/tr><tr><td>Can you enforce referential integrity?<\/td><td><strong>YES.<\/strong>&nbsp;A hard FK can be placed from&nbsp;<code>PersonContact.ContactID<\/code>&nbsp;to&nbsp;<code>Contacts.ContactID<\/code>.<\/td><\/tr><tr><td>Is this a good design?<\/td><td><strong>YES.<\/strong>&nbsp;It is the industry-standard relational solution for this problem.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Bonus: &#8220;Whiteboard&#8221; Scenario (For all levels)<\/h3>\n\n\n\n<p><em>Give them a piece of paper and ask:<\/em><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Draw a logical data model for a <code>University Registration System<\/code>. We have <code>Students<\/code>, <code>Professors<\/code>, <code>Courses<\/code>, and <code>Rooms<\/code>. A Student can enroll in many courses; a Course is taught by one Professor; a Course meets in one Room.&#8221;<\/p>\n<\/blockquote>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Beginner check:<\/strong> Do they draw the 4 tables and establish Foreign Keys?<\/li>\n\n\n\n<li><strong>Intermediate check:<\/strong> Do they realize a <code>Student_Course<\/code> junction table is needed for the many-to-many relationship?<\/li>\n\n\n\n<li><strong>Advanced check:<\/strong> Do they ask clarifying questions? (e.g., <em>&#8220;Can a room be used by multiple courses at different times? If so, we need a Time slot dimension.&#8221;<\/em>). The advanced candidate does not just draw; they define the grain.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here is a comprehensive list of technical data modeling interview questions, categorized by skill level. These questions focus on relational (OLTP) and dimensional (OLAP\/Data Warehousing) modeling, with a touch of NoSQL for the advanced tier. I have also included &#8220;Expected Answer Bullets&#8221; so you know exactly what to listen for. Beginner Level (Conceptual &amp; Logical [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":25732,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[31,63],"tags":[],"class_list":["post-25730","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bi-data-warehouse","category-programming"],"jetpack_featured_media_url":"https:\/\/www.designandexecute.com\/designs\/wp-content\/uploads\/2026\/09\/Data-modeling.jpg","_links":{"self":[{"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/posts\/25730","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/comments?post=25730"}],"version-history":[{"count":5,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/posts\/25730\/revisions"}],"predecessor-version":[{"id":25742,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/posts\/25730\/revisions\/25742"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/media\/25732"}],"wp:attachment":[{"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/media?parent=25730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/categories?post=25730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.designandexecute.com\/designs\/wp-json\/wp\/v2\/tags?post=25730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}