← Thumbtack Interview Insights

Thumbtack·Data Scientist·Technical Phone Screen·Senior

Senior
Jun 2026

Summary

Thumbtack data scientist SQL round, three questions back to back all on the same marketplace schema. Pretty dense for a technical screen, felt like they wanted to see if you could handle window functions and ordered-set aggregates under pressure.

Questions Asked (3)

Q1

Given a marketplace schema with requests, quotes, and categories, compute the 90th percentile of time-to-first-quote (in minutes) per category per calendar week, for requests that received at least one quote.

Product Analytics & MetricsData Modeling
Author's notes

The percentile_cont part wasn't the hard bit.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the schema and defining the metric precisely: time-to-first-quote is the minimum quote timestamp per request minus the request timestamp, in minutes. Then join requests to quotes, filter to requests with at least one quote, and use a window function to compute the first quote time per request before aggregating to the category-week level and calculating the 90th percentile.

Pro tip: Mention that you would validate the percentile calculation by checking for edge cases like requests with multiple quotes in the same minute or missing timestamps, and consider using approximate percentile functions for scalability if the dataset is large.

1. Clarify schema and metric definition

Confirm the table structures, timestamp columns, and how categories are linked to requests. Define time-to-first-quote as the difference between the earliest quote timestamp and the request timestamp, in minutes.

2. Compute first quote time per request

Join requests to quotes on request_id, filter to requests that have at least one quote, and use a window function (e.g., MIN(quote_timestamp) OVER (PARTITION BY request_id)) to get the first quote time for each request.

3. Calculate time-to-first-quote

For each request, compute the time difference in minutes between the first quote timestamp and the request timestamp. Ensure the result is non-negative and handle any nulls appropriately.

4. Aggregate to category-week level

Extract the calendar week from the request timestamp (e.g., using DATE_TRUNC('week', request_timestamp)), group by category and week, and compute the 90th percentile of the time-to-first-quote values.

5. Validate and present results

Check for anomalies, such as weeks with very few requests, and consider using approximate percentiles for large datasets. Present the results with clear labels and note any assumptions.

Key Points to Mention

  • Use of window functions to efficiently compute the first quote per request without self-joins.
  • Filtering to only requests with at least one quote before aggregation.
  • Definition of calendar week (e.g., ISO week or starting on Sunday/Monday) and time zone considerations.
  • Choice of percentile function (exact vs. approximate) and handling of small sample sizes per category-week.
  • Handling of edge cases: requests with quotes before request time, missing timestamps, or multiple quotes at the same time.
  • Importance of indexing or partitioning for performance on large datasets.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

For the 30-day window ending 2025-09-01, find the top 3 pros per category by completed booking count, with ties handled via dense_rank.

Product Analytics & MetricsAlgorithms & Data Structures
Author's notes

Classic ranking question but the join chain is a little annoying here because bookings doesn't have category_id directly.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the schema and definitions (completed bookings, categories, pros), then write a SQL query that filters bookings to the 30-day window ending 2025-09-01, aggregates completed bookings per pro per category, applies DENSE_RANK() partitioned by category ordered by count descending, and selects ranks <= 3. Validate edge cases like ties and missing data.

Pro tip: Mention that DENSE_RANK ensures ties receive the same rank without gaps, and explicitly state how you'd handle ties at the cutoff (e.g., if multiple pros tie for 3rd, all are included). Also, confirm whether 'top 3' means exactly 3 pros or up to 3 ranks, as this affects the output.

1. Clarify requirements and schema

Ask about table structures (bookings, pros, categories), definitions of 'completed' and '30-day window', and whether the window is inclusive of both endpoints. Confirm that 'top 3' means top 3 ranks per category.

2. Filter and aggregate bookings

Filter bookings to completed status and booking dates between 2025-08-03 and 2025-09-01 (inclusive). Group by category and pro to count completed bookings.

3. Apply DENSE_RANK and select top 3

Use DENSE_RANK() OVER (PARTITION BY category ORDER BY completed_bookings DESC) to rank pros within each category. Then select rows where rank <= 3.

4. Validate and discuss edge cases

Check for ties, nulls, and categories with fewer than 3 pros. Explain how DENSE_RANK handles ties and confirm that the output includes all tied pros if they fall within the top 3 ranks.

Key Points to Mention

  • Definition of 'completed booking' (e.g., status = 'completed') and the exact date range (inclusive of 2025-09-01).
  • Use of DENSE_RANK() to handle ties without gaps, ensuring that if two pros tie for 1st, the next rank is 2.
  • Partitioning by category to rank pros within each category independently.
  • Aggregation step: COUNT of completed bookings per pro per category.
  • Handling of ties at the cutoff: if multiple pros tie for 3rd place, all are included (since DENSE_RANK assigns the same rank).
  • Potential data quality issues: missing categories, pros with zero completed bookings, or bookings outside the window.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

For quotes created on 2025-08-31 through 2025-09-01, compute each quote's percent_rank of price within its request, where lower price ranks better, and return only quotes in the cheapest 20%. Break ties by lowest quote_id.

Product Analytics & MetricsAlgorithms & Data Structures
Author's notes

percent_rank with lower-is-better means ordering ASC on price.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, filter the quotes to the specified date range (2025-08-31 through 2025-09-01). Then, for each request, compute the percent_rank of price where lower price ranks better (i.e., ascending order), and finally select only those quotes whose percent_rank is <= 0.20, breaking ties by lowest quote_id.

Pro tip: Clarify whether the date range is inclusive and whether percent_rank should be computed per request or globally; in SQL, use PERCENT_RANK() OVER (PARTITION BY request_id ORDER BY price ASC, quote_id ASC) to handle ties consistently.

1. Filter by date range

Select only quotes where the creation date is between 2025-08-31 and 2025-09-01, inclusive. Ensure the date column is properly formatted and consider time zones if applicable.

2. Compute percent_rank per request

For each request, calculate the percent_rank of each quote's price in ascending order (lower price = better rank). Use a window function like PERCENT_RANK() OVER (PARTITION BY request_id ORDER BY price ASC, quote_id ASC) to handle ties by quote_id.

3. Filter to cheapest 20%

Keep only quotes where the computed percent_rank is less than or equal to 0.20. This selects the cheapest 20% within each request.

4. Handle ties and ordering

Ensure that ties in price are broken by the lowest quote_id, as specified. The ORDER BY clause in the window function should include quote_id as a tiebreaker.

5. Return final result

Output the selected quotes, possibly including request_id, quote_id, price, and percent_rank, ordered as needed for clarity.

Key Points to Mention

  • Use of window functions (e.g., PERCENT_RANK) to compute relative rank within each request.
  • Correct ordering: ascending price for 'lower is better', with quote_id as tiebreaker.
  • Date filtering: inclusive bounds and proper date format.
  • Threshold condition: percent_rank <= 0.20 to get the cheapest 20%.
  • Partitioning by request_id to compute ranks independently per request.
  • Handling ties: ensuring deterministic results by including quote_id in the ORDER BY clause.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.