1. Design a schema to track user interactions with local ads over time.
Declare separate grains for ad impressions, clicks, calls, direction requests, and attributed outcomes. Include stable identifiers for campaign, creative, placement, region, device class, privacy-safe user or session, and event time; preserve repeated legitimate interactions while making client retries detectable. Explain the relationships needed for regional performance reporting without joining facts at mixed grains.
I would use separate event-grain fact tables for impressions, clicks, calls, direction requests, and outcomes, all linked many-to-one to one conformed ad-context dimension. Each event has its own event ID and client retry ID. For reporting, aggregate facts separately to one common grain before combining them.
The main decision is to store each kind of ad activity separately because an ad being shown is not the same event as a click, phone call, direction request, or later business result. Every real interaction should stay as its own record, even when the same person repeats an action. At the same time, accidental resends from a client should be recognizable. Shared information such as campaign, creative, placement, region, and device type should use one consistent definition so regional reports can compare the different activities without accidentally multiplying records or counting the same activity more than intended.
- What should count as an attributed outcome: a purchase, store visit, lead, or several outcome types?
- What time zone should define the reporting date for regional reports?
- How long should client_event_id values remain available for retry detection?
- Can anonymous_user_key or session_id be missing when privacy or collection rules prevent identifying a user or session?
- Is attribution limited to a triggering click, as shown in the supplied model, or may future attribution need to reference other interaction types?
I would start by declaring the grain of every fact table. Grain means exactly what one row represents.
- Fact_Ad_Impression: one row per served impression event.
- Fact_Ad_Click: one row per click event.
- Fact_Ad_Call: one row per call interaction event.
- Fact_Direction_Request: one row per direction-request event.
- Fact_Attributed_Outcome: one row per attributed outcome event.
Each fact has its own event-level primary key: impression_event_id, click_event_id, call_event_id, direction_event_id, or outcome_event_id. This preserves legitimate repeated behavior. If the same user clicks twice, those are two distinct click events rather than one row being overwritten or removed as a duplicate.
Each fact also stores client_event_id. In this model, client_event_id is the retry-detection or idempotency identifier for a client submission. If the same submission is retried, the repeated delivery can be recognized using that identifier. A legitimate second interaction has its own event identity and should not be collapsed merely because the user, campaign, region, or timestamp looks similar.
The shared Dim_Ad_Context table is a conformed dimension. Conformed means the same dimension definition is reused consistently across multiple fact tables. It has the surrogate primary key ad_context_key and stable identifiers for campaign_id, creative_id, placement_id, region_id, and device_class_id. One dimension row represents one unique combination of those ad-context identifiers.
Every fact contains ad_context_key and has a many-to-one relationship to Dim_Ad_Context: many event rows can point to one ad-context row. Each fact also stores event_time and the privacy-safe identifiers anonymous_user_key and session_id shown in the diagram.
The event-specific columns remain only in the facts where they make sense. Fact_Ad_Click contains click_id, such as a GCLID when that identifier is available. Fact_Ad_Call contains call_duration_sec. Fact_Attributed_Outcome contains attribution_click_id and can contain optional order_id and optional outcome_value.
For regional performance reporting, I would not join the raw fact tables directly to each other. They have different grains and different numbers of rows. Joining them through common values such as region or campaign can create fan-out, where one fact row matches several rows from another fact and inflates counts or values.
Instead, I would join each fact independently many-to-one to Dim_Ad_Context and aggregate each fact to the same reporting grain. The diagram uses region_id + date + campaign_id as an example. I would produce separate impression, click, call, direction-request, and outcome aggregates at exactly that grain. Only then would I combine those same-grain aggregate results for reporting.
This design uses more fact tables than a single generic event table, but it makes the business meaning and grain of every row explicit. It also keeps event-specific fields in the correct place and reduces the risk of mixed-grain reporting errors. The operational tradeoff is that ingestion must maintain reliable event IDs, client_event_id retry detection, and consistent ad_context_key lookups. This is a conceptual data-modeling design, so no database-specific SQL dialect or engine behavior is required.
- Identify the five business processes: impression, click, call, direction request, and attributed outcome.
- Declare one event-level grain for each corresponding fact table.
- Give every legitimate interaction its own immutable event primary key.
- Store client_event_id separately so retries of one client submission are detectable without collapsing genuine repeated interactions.
- Store event_time plus the privacy-safe anonymous_user_key and session_id shown in the model.
- Create Dim_Ad_Context with ad_context_key and stable campaign, creative, placement, region, and device-class identifiers.
- Link every fact many-to-one to Dim_Ad_Context using ad_context_key.
- Keep event-specific attributes only on their appropriate facts, such as call_duration_sec or outcome_value.
- For regional reporting, aggregate each fact independently to the same grain, such as region_id + date + campaign_id.
- Combine only those same-grain aggregates instead of joining raw facts together.
Storage grows with the number of legitimate interaction events because impressions, clicks, calls, direction requests, and outcomes are all retained separately. Retry detection adds ingestion work because client_event_id values must be checked or tracked. Regional reporting must scan and aggregate each relevant fact independently before combining results, so query cost depends on the event volume being analyzed. The model has more tables than a single mixed event table, but maintenance is safer because every fact has one clear grain and event-specific columns stay in the correct table.
This question tests whether the candidate can define precise fact-table grains, model shared dimensions, preserve legitimate repeated events, support retry detection, and avoid fan-out and double counting when different interaction facts are combined for regional analytics.
A common mistake is storing impressions, clicks, calls, direction requests, and outcomes in one table without a precise row grain. Another is treating repeated interactions from the same user as duplicates and deleting legitimate second or third events. Using no separate retry identifier makes client resends harder to distinguish from real activity. The most serious reporting mistake is joining raw fact tables together on region, campaign, user, or session, which can create fan-out and inflate counts. Other mistakes include duplicating shared ad-context attributes inconsistently across facts, putting event-specific measures in the dimension, or combining aggregates before every source has been reduced to the same reporting grain.
State the five fact grains first. Then explain the two main correctness rules: client_event_id detects retries without removing legitimate repeated events, and raw facts are not joined at mixed grains. Finish by showing that every fact links many-to-one to the same conformed ad-context dimension and is aggregated independently before regional metrics are combined.









