← TikTok Interview Insights

TikTok·Data Scientist·Technical Phone Screen·Senior

Senior
May 2026

Summary

TikTok data scientist interview focused entirely on SQL for ad safety, with a pretty involved schema and four escalating tasks. The questions were technical and specific enough that you really couldn't wing it.

Questions Asked (4)

Q1

Given an ad safety schema with advertisers, ads, page visits, and reports tables, write a single SQL query to identify 'top bad advertisers' defined as those with at least 1,000 page visits and 50 unique reporters in the last 7 days. Rank them by report rate (total reports divided by total visits), using window functions with deterministic tiebreakers, and return only the top 5 ranks.

Product Analytics & MetricsData Modeling
Author's notes

This is the kind of question where the filtering criteria and the ranking criteria are separate and it's easy to conflate them.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Break the problem into three parts: first, aggregate visits and reports per advertiser over the last 7 days; second, filter advertisers meeting the thresholds of 1,000 visits and 50 unique reporters; third, compute the report rate and use a window function with deterministic tiebreakers to rank and select the top 5. Write a single SQL query using CTEs for clarity and ensure all aggregations are correct.

Pro tip: Always clarify the definition of 'unique reporters' and 'last 7 days' (e.g., relative to current date or a fixed date) and confirm whether visits and reports should be counted from the same time window. Also, use deterministic tiebreakers like advertiser_id to avoid non-deterministic results.

1. Aggregate metrics per advertiser

Use CTEs to compute total visits and total reports per advertiser from the page_visits and reports tables, filtering for the last 7 days. Also compute the number of unique reporters per advertiser.

2. Apply thresholds

Filter the aggregated results to only include advertisers with at least 1,000 page visits and at least 50 unique reporters.

3. Calculate report rate

Compute the report rate as total reports divided by total visits for each qualifying advertiser.

4. Rank with window function

Use a window function (e.g., RANK() or DENSE_RANK()) to rank advertisers by report rate in descending order, with deterministic tiebreakers such as advertiser_id ascending.

5. Select top 5

Filter the ranked results to only include ranks 1 through 5 and return the advertiser details along with the rank.

Key Points to Mention

  • Use of CTEs for readability and modularity
  • Correct aggregation of visits and reports with proper date filtering (e.g., using DATE_SUB or INTERVAL)
  • Counting unique reporters using COUNT(DISTINCT reporter_id)
  • Applying HAVING clause for thresholds after aggregation
  • Using window functions like RANK() or DENSE_RANK() with ORDER BY report_rate DESC, advertiser_id ASC for deterministic ranking
  • Ensuring the final query returns only top 5 ranks and includes necessary columns

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

Q2

Extend the previous advertiser ranking query to also output each advertiser's single worst ad, defined as the ad with the highest report rate in the last 7 days among ads with at least 100 visits. Break ties by total reports descending then ad_id ascending.

Data ModelingProduct Analytics & Metrics
Author's notes

Adding a per-advertiser worst ad without blowing up the row count is the tricky part.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, compute the report rate for each ad over the last 7 days, filtering to ads with at least 100 visits. Then, for each advertiser, select the ad with the highest report rate, breaking ties by total reports descending and ad_id ascending. Finally, join this result with the previous advertiser ranking query to output each advertiser's ranking along with their worst ad.

Pro tip: Clarify the definition of 'report rate' (e.g., reports per visit) and ensure you handle ties correctly using ROW_NUMBER() with the specified ordering. Also, consider the time window: 'last 7 days' should be relative to the current date, not the ad's creation date.

1. Filter ads by visit threshold and time window

Select ads with at least 100 visits in the last 7 days. Ensure the date range is correctly applied to the visits or reports data.

2. Calculate report rate and total reports per ad

Compute report rate as total reports divided by total visits for each ad. Also compute total reports for tie-breaking.

3. Rank ads within each advertiser

Use a window function like ROW_NUMBER() partitioned by advertiser, ordered by report rate descending, total reports descending, and ad_id ascending.

4. Select the worst ad per advertiser

Filter to rows where the row number equals 1 to get the single worst ad for each advertiser.

5. Join with previous advertiser ranking query

Combine the worst ad result with the existing advertiser ranking output, ensuring all advertisers from the ranking are included (use LEFT JOIN if necessary).

Key Points to Mention

  • Definition of report rate: reports per visit, not just total reports.
  • Time window: last 7 days relative to current date, using appropriate date functions.
  • Visit threshold: at least 100 visits to qualify an ad.
  • Tie-breaking logic: highest report rate, then total reports descending, then ad_id ascending.
  • Use of window functions (e.g., ROW_NUMBER) for efficient ranking.
  • Handling advertisers with no qualifying ads (e.g., LEFT JOIN and NULL handling).

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

Q3

Write a query that produces a daily leaderboard for the last 7 days, with one row per advertiser per day, ranked by that day's report rate. Only include advertisers with at least 200 visits on that specific day, and make sure advertisers with zero reports still show up with a rate of zero. Exclude days where an advertiser had no visits at all.

Product Analytics & MetricsData Modeling
Author's notes

The zero-report case is a classic LEFT JOIN trap.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the definitions of 'report rate' and 'visits', then outline a SQL query that aggregates daily visits and reports per advertiser, filters for at least 200 visits, and computes the rate. Use a date spine or calendar table to ensure all last 7 days are covered, and left join to include advertisers with zero reports. Finally, rank advertisers within each day by report rate.

Pro tip: Mention the importance of handling edge cases like advertisers with zero visits (excluded) and zero reports (included with rate 0), and discuss how to optimize the query for performance on large datasets, such as using partitioning or indexing.

1. Clarify Requirements

Confirm definitions: 'report rate' likely means reports per visit (or percentage), 'visits' are user interactions, and 'last 7 days' includes today or yesterday. Ensure understanding of zero-report inclusion and zero-visit exclusion.

2. Design Query Structure

Plan to aggregate visits and reports per advertiser per day, then filter for visits >= 200. Use a calendar table to generate all dates in the last 7 days and left join to include advertisers with zero reports.

3. Compute Metrics and Rank

Calculate report rate as reports/visits (or reports per 100 visits). Use window functions like RANK() or DENSE_RANK() partitioned by date and ordered by report rate descending.

4. Handle Edge Cases

Ensure advertisers with zero reports appear with rate 0, and exclude days where an advertiser had no visits. Consider using COALESCE to handle nulls from left joins.

5. Validate and Optimize

Test the query on sample data, check for correctness, and discuss potential performance improvements like indexing on date and advertiser_id.

Key Points to Mention

  • Definition of report rate: reports divided by visits, possibly multiplied by 100 for percentage.
  • Use of a date spine or calendar table to ensure all 7 days are represented.
  • Filtering condition: visits >= 200 per advertiser per day.
  • Left join to include advertisers with zero reports, with COALESCE to set rate to 0.
  • Window function (e.g., RANK() OVER (PARTITION BY date ORDER BY report_rate DESC)) for ranking.
  • Exclusion of days with zero visits for an advertiser, which naturally happens if no row exists in the visits aggregation.

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

Q4

In comments within your query, explain how the approach avoids double-counting when a user both visits and reports the same ad within the same minute, and how it handles report rows that have no corresponding visit row in the time window.

Data ModelingTechnical Trade-offs
Author's notes

This one's more conceptual than coding.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that the query should first aggregate visits and reports at the user-ad-minute grain, then combine them using a full outer join or union with deduplication logic. Use conditional aggregation to count a user-ad-minute as a single event if either a visit or report exists, avoiding double-counting. For report rows without a visit, ensure they are included by treating the report as the event of record in that minute.

Pro tip: Mention that in ad tech, a report often implies a visit, but not always; explicitly state your assumption about the relationship and how you'd validate it with data profiling. This shows you think about data quality and business context.

1. Define the grain and event

Clarify that the analysis is at the user-ad-minute level, and define a 'unique event' as any minute where a user either visited or reported the ad. This sets the foundation for deduplication.

2. Aggregate source tables

Aggregate visits and reports separately to the user-ad-minute grain, using COUNT(DISTINCT) or GROUP BY to collapse multiple actions within the same minute into a single row per source.

3. Combine with full outer join

Use a FULL OUTER JOIN on user_id, ad_id, and minute to merge the aggregated visits and reports, ensuring that report rows without visits are retained and vice versa.

4. Deduplicate with conditional logic

Create a flag or use COALESCE to mark the combined row as a single event if either source exists, and count it once. For example, COUNT(DISTINCT CONCAT(user_id, ad_id, minute)) or SUM(CASE WHEN visit IS NOT NULL OR report IS NOT NULL THEN 1 ELSE 0 END).

5. Validate and document assumptions

Check edge cases like multiple reports in the same minute or reports without visits, and document assumptions about whether a report implies a visit. This ensures the logic is robust and transparent.

Key Points to Mention

  • Grain definition: user-ad-minute as the atomic unit for deduplication.
  • Use of FULL OUTER JOIN to handle missing matches in either table.
  • Conditional aggregation (e.g., CASE WHEN) to count a combined event only once.
  • Handling of report rows without visits by including them as standalone events.
  • Potential need for DISTINCT counts to avoid duplicates from multiple actions in the same minute.
  • Assumption validation: whether a report always implies a visit, and how to check with data.

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