Coding questions · Meta
Write advanced SQL for sales support analytics
This question evaluates practical SQL data-manipulation skills such as joins, aggregation, window functions, time-based filtering, deduplication, and computation of sales KPIs for rep-level analytics.
Write SQL for the following schema and tasks. Assume ANSI SQL with DATE_TRUNC and INTERVAL supported. Sample tables (minimal rows shown).
accounts
+------------+-----------+------------+---------+
| account_id | industry | size_bucket| region |
+------------+-----------+------------+---------+
| 100 | Software | MM | West |
| 101 | Healthcare| SMB | East |
| 102 | Manufacturing| ENT | East |
| 103 | Retail | MM | Central |
| 104 | Finance | SMB | Central |
+------------+-----------+------------+---------+
reps
+--------+------------+--------+---------+----------+
| rep_id | hire_date | segment| region | quota_usd|
+--------+------------+--------+---------+----------+
| 10 | 2024-09-01 | SMB | West | 500000 |
| 11 | 2024-12-15 | MM | East | 800000 |
| 12 | 2025-03-10 | SMB | Central | 400000 |
+--------+------------+--------+---------+----------+
deals
+---------+------------+--------+---------------------+---------------------+------------+--------+------------+---------+
| deal_id | account_id | rep_id | created_at | closed_at | stage | is_won | amount_usd | region |
+---------+------------+--------+---------------------+---------------------+------------+--------+------------+---------+
| 1 | 100 | 10 | 2025-04-05 10:00:00 | 2025-04-20 09:00:00 | Closed Won | 1 | 12000 | West |
| 2 | 100 | 10 | 2025-04-25 12:00:00 | 2025-05-10 08:00:00 | Closed Lost| 0 | 8000 | West |
| 3 | 101 | 11 | 2025-05-02 11:00:00 | 2025-06-01 16:00:00 | Closed Won | 1 | 5000 | East |
| 4 | 102 | 11 | 2025-06-15 09:00:00 | 2025-07-20 10:00:00 | Closed Lost| 0 | 20000 | East |
| 5 | 103 | 12 | 2025-06-10 14:00:00 | 2025-06-22 10:00:00 | Closed Won | 1 | 15000 | Central |
| 6 | 104 | 12 | 2025-07-01 08:30:00 | 2025-07-18 12:00:00 | Closed Lost| 0 | 0 | Central |
+---------+------------+--------+---------------------+---------------------+------------+--------+------------+---------+
touches (duplicates possible on same day/channel)
+----------+------------+--------+------------+---------+--------------------+
| touch_id | account_id | rep_id | touch_date | channel | is_primary_contact |
+----------+------------+--------+------------+---------+--------------------+
| 1 | 100 | 10 | 2025-01-10 | email | 1 |
| 2 | 100 | 10 | 2025-04-01 | call | 1 |
| 3 | 100 | 10 | 2025-04-01 | call | 1 |
| 4 | 101 | 11 | 2025-02-15 | webinar | 0 |
| 5 | 101 | 11 | 2025-04-28 | call | 1 |
| 6 | 102 | 11 | 2025-06-16 | email | 1 |
| 7 | 103 | 12 | 2025-06-01 | call | 1 |
| 8 | 104 | 12 | 2025-06-29 | email | 1 |
+----------+------------+--------+------------+---------+--------------------+
Tasks: A) For Q2 2025 (2025-04-01 to 2025-06-30 inclusive), by rep_id compute: closed_deals, wins, win_rate, avg_deal_amount_usd, avg_sales_cycle_days. Include only deals with stage like 'Closed%' and amount_usd > 0. Return rows sorted by win_rate desc; break ties by higher avg_deal_amount_usd. B) Deduplicate touches by keeping the smallest touch_id per (account_id, touch_date, channel). Define first_touch_channel_2025 as the channel of the earliest deduped touch for that account in 2025. Join to deals closed in 2025 and produce a table: first_touch_channel, segment (from reps), closed_deals, wins, segment_win_rate, and then a final row per channel showing the segment-weighted win_rate across segments (weights = that channel's closed_deals per segment). Provide a single SQL that outputs both per-segment and overall rows (e.g., via GROUPING SETS or UNION ALL). C) Compute, for each rep, the Pearson correlation r across Q2 2025 weeks between prior-week call_count (channel='call') and current-week win_rate. Define week as DATE_TRUNC('week', date). Show SQL that: (1) builds weekly call counts; (2) builds weekly win_rate for deals closed that week; (3) lags call_count by 1 week within rep; (4) aggregates per rep over weeks to compute r using the closed-form formula r = (nsum(xy)-sum(x)sum(y)) / sqrt((nsum(x^2)-sum(x)^2)(nsum(y^2)-sum(y)^2)). Return rep_id and r, excluding reps with n < 5 weeks of overlapping data. D) Data quality checks: (i) list deals where closed_at < created_at; (ii) accounts whose first touch occurs after their first close date; (iii) any deal with region differing from its account's region. Provide SQL for each. E) Last-7-days activity as of today: Use today = '2025-09-01'. For the window 2025-08-26 to 2025-09-01 inclusive, list top 3 reps by count of distinct accounts touched via calls (deduped rule above). Break ties by higher total call count, then lower rep_id. Provide SQL.
Rep-level Q2 2025 win-rate and sales-cycle metrics
Using the schema below, write a SQL query to compute per-rep performance metrics for Q2 2025 (from 2025-04-01 to 2025-06-30 inclusive).
For each rep_id, consider only deals that:
- Have closed_at between DATE '2025-04-01' and DATE '2025-06-30' (inclusive),
- Have stage LIKE 'Closed%', and
- Have amount_usd > 0.
For those deals, output one row per rep with:
- rep_id
- closed_deals: count of qualifying closed deals
- wins: count of qualifying deals where is_won = 1
- win_rate: wins / closed_deals (as a decimal)
- avg_deal_amount_usd: average amount_usd over qualifying deals
- avg_sales_cycle_days: average number of days between created_at and closed_at (closed_date - created_date)
Return rows sorted by win_rate in descending order; break ties by higher avg_deal_amount_usd (also descending).
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
One row per rep_id showing Q2 2025 closed-deal counts, wins, win_rate, average deal amount, and average sales cycle in days, sorted by win_rate DESC then avg_deal_amount_usd DESC.
| avg_deal_amount_usd | avg_sales_cycle_days | closed_deals | rep_id | win_rate | wins |
|---|---|---|---|---|---|
| 15000 | 12 | 1 | 12 | 1 | 1 |
| 5000 | 30 | 1 | 11 | 1 | 1 |
| 10000 | 15 | 2 | 10 | 0.5 | 1 |
Hints
- Filter deals to Q2 2025 using closed_at and the given stage and amount constraints before aggregating.
- Use date subtraction (closed_date - created_date) to get sales cycle in days, and cast sums to decimals to avoid integer division in win_rate.
First-touch channel vs. win-rate by segment with channel totals
Using the schema below, write a single SQL query to analyze win rates by first-touch channel and rep segment for deals closed in 2025.
Steps:
- Deduplicate touches by keeping only the row with the smallest touch_id per (account_id, touch_date, channel).
- For each account, within calendar year 2025, define first_touch_channel_2025 as the channel of that account's earliest deduplicated touch in 2025 (smallest touch_date; break same-day ties by smallest touch_id).
- Consider all deals whose closed_at is in 2025 (any stage). Join each deal to its account's first_touch_channel_2025 and to the reps table for the segment.
- Produce a result set with columns:
- first_touch_channel
- segment (from reps)
- closed_deals: count of deals for that (first_touch_channel, segment)
- wins: count of deals with is_won = 1
- segment_win_rate: wins / closed_deals
- In the same query, also output a final aggregated row per first_touch_channel that shows the segment-weighted win_rate across segments (i.e., overall wins / overall closed_deals per channel). For these rows, you may set segment = 'ALL'.
Use GROUPING SETS or a similar technique so that both per-segment rows and per-channel total rows are returned from a single SQL statement.
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
Rows at two levels of aggregation: (first_touch_channel, segment) and (first_touch_channel, 'ALL'). For each row, closed_deals is the number of deals closed in 2025 for that grouping, wins is the number with is_won = 1, and segment_win_rate = wins / closed_deals. 'ALL' rows show the segment-weighted win_rate per channel.
| closed_deals | first_touch_channel | segment | segment_win_rate | wins |
|---|---|---|---|---|
| 1 | call | SMB | 1 | 1 |
| 1 | call | ALL | 1 | 1 |
| 1 | MM | 0 | 0 | |
| 3 | SMB | 0.3333 | 1 | |
| 4 | ALL | 0.25 | 1 | |
| 1 | webinar | MM | 1 | 1 |
| 1 | webinar | ALL | 1 | 1 |
Hints
- Use ROW_NUMBER() to first deduplicate touches per (account_id, touch_date, channel), then again to pick the earliest 2025 touch per account.
- After joining deals to first-touch channels and reps, use GROUPING SETS (or CUBE/ROLLUP) to get both per-segment rows and per-channel totals in a single aggregation.
Weekly Q2 2025 correlation between prior-week calls and win rate
Using the schema below, compute for each rep the Pearson correlation coefficient r across Q2 2025 weeks between prior-week call_count and current-week win_rate.
Definitions:
- Consider only Q2 2025 dates: from DATE '2025-04-01' to DATE '2025-06-30' inclusive.
- A week is defined as week_start = DATE_TRUNC('week', date).
- call_count for a rep and week is the number of touches with channel = 'call' whose touch_date falls in that calendar window; group them by rep_id and week_start.
- For win_rate, consider deals where closed_at is in Q2 2025, stage LIKE 'Closed%', and amount_usd > 0. For each rep_id and week_start = DATE_TRUNC('week', closed_at::date), compute win_rate = wins / closed_deals.
- For each rep and week, define x = prior-week call_count (i.e., call_count for the previous week_start within that rep), and y = current-week win_rate.
Tasks:
- Build weekly_call_counts: (rep_id, week_start, call_count) for Q2 2025 call touches.
- Build weekly_win_rates: (rep_id, week_start, win_rate) for Q2 2025 closed deals.
- Within each rep, lag call_count by week_start to get prior_week_call_count, and join it to the matching weekly_win_rates for the same week, forming pairs (x, y).
- For each rep, over all weeks where both x and y are non-null, compute the Pearson correlation r using the closed-form formula: r = (nsum(xy) - sum(x)sum(y)) / sqrt((nsum(x^2) - sum(x)^2) * (nsum(y^2) - sum(y)^2))
- Return rep_id and r, but exclude any reps where n < 5 (fewer than 5 weeks with overlapping data).
Write a single SQL query (using CTEs is fine) that performs steps (1)–(5).
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
One row per rep_id with the Pearson correlation r between prior-week call_count and current-week win_rate for Q2 2025 weeks, excluding reps with fewer than 5 overlapping weeks. With the minimal sample data provided, no rep has at least 5 overlapping weeks, so the result set is empty.
Hints
- Build weekly call counts and weekly win rates in separate CTEs using DATE_TRUNC('week', ...), then use a window LAG on call_count to get prior_week_call_count per rep.
- After joining prior-week call counts to current-week win rates, aggregate per rep to compute the sums needed for the Pearson correlation closed-form formula and filter out reps with n < 5.
Data quality check: deals closed before they were created
Write a SQL query to list all deals where closed_at is earlier than created_at, indicating an invalid sales cycle.
Return all columns from the deals table for such records.
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
All deals where closed_at < created_at. With the provided sample data there are no such rows, so the result is empty.
Hints
- Compare closed_at directly to created_at in a simple WHERE clause.
- You can SELECT * since the requirement is to list all details of problematic deals.
Data quality check: first touch after first close
Write a SQL query to find accounts whose first recorded touch occurs after their first recorded deal close date.
For each such account, output:
- account_id
- first_touch_date: the earliest touch_date in touches for that account
- first_close_date: the earliest closed_at date (cast to DATE) in deals for that account
Only include accounts that have at least one touch and at least one deal.
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
Accounts where the earliest touch_date is later than the earliest closed_at date. With the provided sample data there are no such accounts, so the result is empty.
Hints
- Compute the earliest touch_date and earliest closed_at date per account in separate CTEs, then join them.
- Filter to rows where first_touch_date is greater than first_close_date to catch accounts touched only after their first closing.
Data quality check: deal region mismatches account region
Write a SQL query to identify any deals whose region value does not match the region of their associated account.
For each such deal, output:
- deal_id
- account_id
- deal_region (from deals.region)
- account_region (from accounts.region)
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
Deals whose region does not equal their account's region. With the provided sample data, no deals violate this rule, so the result set is empty.
Hints
- Join deals to accounts on account_id to compare the two region columns.
- Filter rows where the region values differ and select the requested columns.
Last-7-days call activity: top reps by distinct accounts touched
Assume today is DATE '2025-09-01'. For the 7-day window from DATE '2025-08-26' to DATE '2025-09-01' inclusive, write a SQL query to list the top 3 reps by outreach via calls.
Rules:
- Work only with touches where channel = 'call' and touch_date between DATE '2025-08-26' and DATE '2025-09-01' inclusive.
- Deduplicate touches by keeping only the row with the smallest touch_id per (account_id, touch_date, channel).
- For each rep_id, compute:
- distinct_accounts_touched: COUNT(DISTINCT account_id) over these deduplicated call touches.
- total_calls: COUNT(*) over these deduplicated call touches.
- Return the top 3 reps ordered by:
- distinct_accounts_touched DESC,
- total_calls DESC,
- rep_id ASC (as tie-breaker).
Output columns:
- rep_id
- distinct_accounts_touched
- total_calls
Table accounts
| column | type | constraints |
|---|---|---|
| account_id | INT | PRIMARY KEY |
| industry | VARCHAR(50) | NOT NULL |
| size_bucket | VARCHAR(10) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | industry | region | size_bucket |
|---|---|---|---|
| 100 | Software | West | MM |
| 101 | Healthcare | East | SMB |
| 102 | Manufacturing | East | ENT |
| 103 | Retail | Central | MM |
| 104 | Finance | Central | SMB |
Table reps
| column | type | constraints |
|---|---|---|
| rep_id | INT | PRIMARY KEY |
| hire_date | DATE | NOT NULL |
| segment | VARCHAR(20) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
| quota_usd | DECIMAL(12,2) | NOT NULL |
Sample data:
| hire_date | quota_usd | region | rep_id | segment |
|---|---|---|---|---|
| 2024-09-01 | 500000 | West | 10 | SMB |
| 2024-12-15 | 800000 | East | 11 | MM |
| 2025-03-10 | 400000 | Central | 12 | SMB |
Table deals
| column | type | constraints |
|---|---|---|
| deal_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| created_at | TIMESTAMP | NOT NULL |
| closed_at | TIMESTAMP | NOT NULL |
| stage | VARCHAR(50) | NOT NULL |
| is_won | INT | NOT NULL |
| amount_usd | DECIMAL(12,2) | NOT NULL |
| region | VARCHAR(20) | NOT NULL |
Sample data:
| account_id | amount_usd | closed_at | created_at | deal_id | is_won | region | rep_id | stage |
|---|---|---|---|---|---|---|---|---|
| 100 | 12000 | 2025-04-20 09:00:00 | 2025-04-05 10:00:00 | 1 | 1 | West | 10 | Closed Won |
| 100 | 8000 | 2025-05-10 08:00:00 | 2025-04-25 12:00:00 | 2 | 0 | West | 10 | Closed Lost |
| 101 | 5000 | 2025-06-01 16:00:00 | 2025-05-02 11:00:00 | 3 | 1 | East | 11 | Closed Won |
| 102 | 20000 | 2025-07-20 10:00:00 | 2025-06-15 09:00:00 | 4 | 0 | East | 11 | Closed Lost |
| 103 | 15000 | 2025-06-22 10:00:00 | 2025-06-10 14:00:00 | 5 | 1 | Central | 12 | Closed Won |
| 104 | 0 | 2025-07-18 12:00:00 | 2025-07-01 08:30:00 | 6 | 0 | Central | 12 | Closed Lost |
Table touches
| column | type | constraints |
|---|---|---|
| touch_id | INT | PRIMARY KEY |
| account_id | INT | NOT NULL |
| rep_id | INT | NOT NULL |
| touch_date | DATE | NOT NULL |
| channel | VARCHAR(20) | NOT NULL |
| is_primary_contact | INT | NOT NULL |
Sample data:
| account_id | channel | is_primary_contact | rep_id | touch_date | touch_id |
|---|---|---|---|---|---|
| 100 | 1 | 10 | 2025-01-10 | 1 | |
| 100 | call | 1 | 10 | 2025-04-01 | 2 |
| 100 | call | 1 | 10 | 2025-04-01 | 3 |
| 101 | webinar | 0 | 11 | 2025-02-15 | 4 |
| 101 | call | 1 | 11 | 2025-04-28 | 5 |
| 102 | 1 | 11 | 2025-06-16 | 6 | |
| 103 | call | 1 | 12 | 2025-06-01 | 7 |
| 104 | 1 | 12 | 2025-06-29 | 8 | |
| 100 | call | 1 | 10 | 2025-08-26 | 9 |
| 101 | call | 1 | 11 | 2025-08-27 | 10 |
| 101 | call | 1 | 11 | 2025-08-27 | 11 |
| 102 | 1 | 11 | 2025-08-28 | 12 | |
| 103 | call | 1 | 12 | 2025-08-29 | 13 |
| 103 | call | 1 | 12 | 2025-08-30 | 14 |
| 104 | call | 1 | 12 | 2025-08-30 | 15 |
| 104 | call | 1 | 12 | 2025-09-01 | 16 |
Expected output
Top 3 reps in the window 2025-08-26 to 2025-09-01 by distinct accounts touched via call (after deduplication), with ties broken by total_calls then rep_id ascending.
| distinct_accounts_touched | rep_id | total_calls |
|---|---|---|
| 2 | 12 | 4 |
| 1 | 10 | 1 |
| 1 | 11 | 1 |
Hints
- First deduplicate call touches in the 2025-08-26 to 2025-09-01 window using ROW_NUMBER() over (account_id, touch_date, channel).
- Aggregate per rep to get distinct account count and total_calls, then apply the specified ORDER BY and limit to 3 rows.