← Amazon Interview Insights

Amazon·Data Scientist·Technical Phone Screen·Senior

Senior
Jun 2026

Summary

SQL-heavy technical screen for a Data Science role at Amazon. One big multi-part query covering window functions, LEFT JOINs, and some tricky edge cases. The kind of question that looks manageable until you're actually writing it.

Questions Asked (3)

Q1

Write a single ANSI SQL query (CTEs allowed, no temp tables) that returns each customer's top 2 product categories by total spend, including ties at rank 2, customers with no orders as a null row, and correct use of RANK() with a specific tiebreaker on earliest order date.

Data ModelingAlgorithms & Data StructuresTechnical Trade-offs
Author's notes

This one took me longer than I'd like to admit to even parse.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Break the problem into stages: first aggregate total spend per customer per category, then rank categories within each customer using RANK() with the specified tiebreaker (earliest order date), filter for ranks 1 and 2, and finally left join back to the customer list to include customers with no orders as a null row. Use CTEs to keep the query modular and readable, and explicitly handle ties at rank 2 by relying on RANK() rather than ROW_NUMBER().

Pro tip: Explicitly state that RANK() is chosen over DENSE_RANK() or ROW_NUMBER() because the requirement is to include ties at rank 2, and that the tiebreaker (earliest order date) is applied within the ORDER BY of the window function to ensure deterministic ranking. Also mention that a LEFT JOIN from the customer table is essential to preserve customers with no orders.

1. Aggregate spend per customer and category

Write a CTE that joins orders, order items, and products to compute total spend per customer per category, and also capture the earliest order date for each customer-category pair as the tiebreaker.

2. Rank categories within each customer

In a second CTE, apply RANK() OVER (PARTITION BY customer_id ORDER BY total_spend DESC, earliest_order_date ASC) to assign a rank to each category per customer, ensuring ties at rank 2 are preserved.

3. Filter top 2 categories

Select from the ranked CTE where rank <= 2, which includes all categories tied at rank 2.

4. Include customers with no orders

LEFT JOIN the filtered results to the customers table so that customers with no orders appear with NULL values for category and spend.

5. Final output and ordering

Select customer_id, category, total_spend, and rank, ordering by customer_id and rank for clarity, and ensure the query is ANSI SQL compliant with no temp tables.

Key Points to Mention

  • Use of RANK() instead of ROW_NUMBER() or DENSE_RANK() to correctly handle ties at rank 2.
  • Inclusion of the earliest order date as a tiebreaker in the ORDER BY clause of the window function.
  • Aggregation of total spend per customer per category using SUM() over order line items.
  • LEFT JOIN from the customer table to include customers with no orders as a null row.
  • Use of CTEs for modularity and readability, avoiding temp tables.
  • Awareness of ANSI SQL compliance and potential performance considerations for large datasets.

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

Q2

Extend the query to add a percent_of_total column showing each category's share of that customer's total spend, ensuring it shows 0 for customers with no orders.

Product Analytics & MetricsData Modeling
Author's notes

The percent_of_total column tripped me up because SUM(...) OVER (PARTITION BY customer_id) on a NULL spend row doesn't naturally give you 0, it gives you NULL.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the existing query structure and the grain of the result set, then use a window function to compute each category's share of the customer's total spend. Ensure customers with no orders are handled via a LEFT JOIN and COALESCE to return 0 instead of NULL.

Pro tip: Mention that you would validate the denominator is the customer's total spend across all categories, not just the categories present, and that you'd test edge cases like customers with zero orders and single-category customers.

1. Clarify the existing query and data model

Ask about the current query, table structures, and how customers, orders, and categories relate. Confirm the grain of the result (e.g., one row per customer per category).

2. Compute total spend per customer

Use a window function like SUM(spend) OVER (PARTITION BY customer_id) to calculate each customer's total spend across all categories.

3. Calculate percent of total

Divide each category's spend by the customer's total spend, using NULLIF to avoid division by zero, and multiply by 100 if a percentage is desired.

4. Handle customers with no orders

Use a LEFT JOIN from customers to orders so customers with no orders appear, and wrap the percent calculation in COALESCE(..., 0) to show 0 instead of NULL.

5. Validate and test edge cases

Check that percentages sum to 100% per customer (when orders exist), and test customers with zero orders, single orders, and multiple categories.

Key Points to Mention

  • Use of window functions (SUM OVER PARTITION BY) to compute total spend per customer without collapsing rows.
  • Handling division by zero with NULLIF or CASE to avoid errors.
  • Using LEFT JOIN to include customers with no orders and COALESCE to default to 0.
  • Ensuring the denominator is the customer's total spend across all categories, not just the current category.
  • Considering performance implications of window functions on large datasets and potential indexing.
  • Validating results by checking that percentages sum to 100% for customers with orders.

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

Q3

Further filter the results to return only customers whose top category's percent_of_total is under 50%, meaning no single category dominates their spend.

Product Analytics & MetricsRoot Cause Analysis
Author's notes

Honestly the cleanest part of the whole thing.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the data model and the definition of 'top category' and 'percent_of_total'. Then outline a SQL query that computes each customer's top category and its percentage, and finally applies a filter to keep only those with percentage under 50%. Emphasize the use of window functions and proper aggregation to avoid errors.

Pro tip: Mention that you would validate the filter by checking edge cases, such as customers with exactly 50% or ties in top category, and ensure the query is efficient by using CTEs or subqueries appropriately.

1. Clarify requirements and data schema

Confirm the table structure, how categories are defined, and what 'percent_of_total' represents (e.g., spend per category divided by total spend).

2. Compute per-customer category totals and percentages

Aggregate spend by customer and category, then calculate each category's percentage of the customer's total spend.

3. Identify top category per customer

Use a window function like ROW_NUMBER() or RANK() partitioned by customer, ordered by percentage descending, to select the top category.

4. Filter customers based on top category percentage

Apply a WHERE clause to keep only customers whose top category's percent_of_total is less than 50%.

5. Validate and optimize

Check results for correctness (e.g., ties, boundary cases) and consider performance implications, using CTEs or temporary tables if needed.

Key Points to Mention

  • Use of window functions (e.g., ROW_NUMBER, RANK) to identify top category per customer
  • Calculation of percent_of_total as category spend divided by total customer spend
  • Filtering with a HAVING or WHERE clause on the computed percentage
  • Handling ties in top category (e.g., using RANK or DENSE_RANK)
  • Edge case: customers with exactly 50% should be excluded (strictly under 50%)
  • Performance considerations: using CTEs, indexing, or avoiding repeated subqueries

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