← Bank of America Interview Insights

Bank of America·Data Scientist·Technical Phone Screen·Intermediate

Intermediate
May 2026

Summary

SQL-heavy technical screen for a data scientist role, all four questions were variations on the same dataset about users and events. Felt like a real take-home vibe but done live, which made the window function stuff more stressful than it needed to be.

Questions Asked (4)

Q1

Given a users table and an events table, compute the 7-day conversion rate by country as of a fixed date. Define conversion rate as users with at least one purchase divided by users with at least one visit in the same window, counting each user at most once in numerator and denominator.

Product Analytics & MetricsData Modeling
Author's notes

This took me longer than I expected to set up correctly.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify the schema and definitions: what constitutes a visit and a purchase, how to handle users with multiple events, and the exact 7-day window relative to the fixed date. Then, write a SQL query that aggregates events per user per country within the window, flags users with at least one visit and at least one purchase, and finally computes the conversion rate as the ratio of distinct purchasing users to distinct visiting users, grouped by country.

Pro tip: Mention that you would validate the conversion rate by checking edge cases like users with purchases but no visits (which should be excluded from both numerator and denominator) and ensure the denominator only includes users with at least one visit. Also, discuss how to handle users with multiple countries (e.g., take the country from their first event or user profile) to avoid double-counting.

1. Clarify requirements and assumptions

Confirm the definition of a 7-day window (e.g., the 7 days ending on the fixed date), what constitutes a visit and a purchase, and how to assign a country to a user if they have events from multiple countries.

2. Filter and aggregate events

Filter the events table to the 7-day window and aggregate by user and country, creating flags for whether the user had at least one visit and at least one purchase.

3. Compute numerator and denominator

Count distinct users with at least one purchase (numerator) and distinct users with at least one visit (denominator) per country, ensuring each user is counted once in each metric.

4. Calculate conversion rate

Divide the numerator by the denominator for each country, handling division by zero (e.g., using NULLIF or CASE) to avoid errors.

5. Validate and interpret results

Check for anomalies, such as countries with zero visits, and consider whether the conversion rate should be expressed as a percentage. Discuss any limitations or assumptions.

Key Points to Mention

  • Use DISTINCT counts to ensure each user is counted at most once in numerator and denominator.
  • Define the 7-day window precisely (e.g., date >= fixed_date - INTERVAL '6 days' AND date <= fixed_date).
  • Handle users with multiple countries by selecting a primary country (e.g., from user profile or first event) to avoid double-counting.
  • Exclude users who have purchases but no visits from both numerator and denominator, as the denominator is defined as users with at least one visit.
  • Use conditional aggregation (e.g., MAX(CASE WHEN event_type = 'purchase' THEN 1 ELSE 0 END)) to flag users with purchases and visits.
  • Consider performance implications and indexing on date and user_id for large datasets.

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

Q2

For each user, return their first purchase date and the number of days between signup and first purchase. Use window functions to handle any duplicate events.

Data ModelingProduct Analytics & Metrics
Author's notes

The window function requirement felt a bit forced here since MIN() in a GROUP BY would've done the job.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Use a window function like ROW_NUMBER() partitioned by user_id and ordered by event timestamp to deduplicate events and identify the first purchase per user. Then join to the signup table and compute the date difference (e.g., DATEDIFF) between signup date and first purchase date. Ensure the query handles users with no purchases appropriately (e.g., left join).

Pro tip: Always clarify the grain of the data and whether 'first purchase' means the earliest purchase event or the earliest purchase after signup. Also, consider time zones and date truncation to avoid off-by-one errors in day calculations.

1. Understand the data model

Identify the relevant tables (e.g., users, events) and columns (user_id, event_type, event_timestamp, signup_date). Confirm the definition of 'first purchase' and 'days between' (calendar days vs. 24-hour periods).

2. Deduplicate and rank events

Use ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp) to assign a rank to each purchase event per user. Filter to rank = 1 to get the first purchase.

3. Join with signup data

Left join the first purchase result to the users table on user_id to retain users without purchases. Ensure signup_date is available for each user.

4. Calculate days between

Compute the difference between first_purchase_date and signup_date using DATEDIFF or equivalent, handling NULLs for users without purchases (e.g., return NULL or 0).

5. Validate and format output

Check for edge cases (e.g., purchases before signup, duplicate signups) and ensure the output includes user_id, first_purchase_date, and days_to_first_purchase.

Key Points to Mention

  • Use of window functions (ROW_NUMBER, RANK, DENSE_RANK) for deduplication and first event selection
  • Handling of duplicate events and ensuring one row per user
  • Date functions (DATEDIFF, DATE_TRUNC) and time zone considerations
  • Left join to include users with no purchases
  • Edge cases: purchases before signup, multiple signups, null timestamps
  • Performance considerations: partitioning, indexing, and avoiding full table scans

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

Q3

Using a self join on the events table, identify users who had at least one visit that occurred strictly before their first purchase.

Data ModelingAlgorithms & Data Structures
Author's notes

Self joins always make my brain do a little stutter.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the schema of the events table, especially the columns for user ID, event type, and timestamp. Then explain how a self join can pair each user's purchase events with their visit events, filtering for visits that occur strictly before the first purchase. Finally, use a subquery or window function to identify the first purchase per user and select distinct users meeting the condition.

Pro tip: Mention that a self join can be inefficient on large datasets and suggest an alternative using window functions (e.g., MIN(CASE WHEN event_type='purchase' THEN timestamp END) OVER (PARTITION BY user_id)) for better performance, showing awareness of scalability.

1. Understand the table schema

Identify the columns: user_id, event_type (e.g., 'visit', 'purchase'), and timestamp. Confirm that each row represents an event.

2. Find first purchase per user

Use a subquery or window function to compute the earliest purchase timestamp for each user.

3. Self join to compare visits and first purchase

Join the events table to itself on user_id, where one side represents visits and the other represents the first purchase. Filter for visit timestamps strictly less than the first purchase timestamp.

4. Select distinct users

Return the distinct user_ids that satisfy the condition, ensuring no duplicates.

5. Consider edge cases and performance

Discuss handling users with no purchases, ties in timestamps, and the efficiency of self joins versus window functions.

Key Points to Mention

  • Self join syntax and join condition on user_id
  • Filtering for event_type = 'visit' and event_type = 'purchase'
  • Using MIN() or ROW_NUMBER() to identify first purchase
  • Strict inequality (<) for timestamp comparison
  • DISTINCT to avoid duplicate users
  • Performance considerations and alternative approaches (e.g., window functions)

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

Q4

Explain how the choice between LEFT JOIN and RIGHT JOIN would change the conversion rate results if certain countries have no purchases during the window.

Technical Trade-offsData Modeling
Author's notes

This was more conceptual and I actually liked it.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the join direction and the definition of conversion rate, then explain how LEFT JOIN preserves all countries (including those with no purchases) while RIGHT JOIN preserves all purchases (potentially excluding countries with no purchases). Show that the choice affects the denominator and numerator, leading to different conversion rates, especially when some countries have zero purchases.

Pro tip: Emphasize that the business question should drive the join choice: if you need to report on all countries (even those with no activity), use LEFT JOIN; if you only care about countries with purchases, RIGHT JOIN might suffice. Always validate with a quick count of distinct countries before and after the join.

1. Clarify the join direction and table roles

Identify which table is on the left and right in the join. Typically, the countries table is left and purchases table is right, but confirm with the interviewer.

2. Define conversion rate formula

State the conversion rate as (number of purchases / number of visitors) or similar, and specify how the join affects the numerator and denominator.

3. Analyze LEFT JOIN impact

With LEFT JOIN, all countries are kept; countries with no purchases will have NULL purchase counts, which may be treated as 0, thus including them in the denominator and potentially lowering the overall conversion rate.

4. Analyze RIGHT JOIN impact

With RIGHT JOIN, only countries with at least one purchase are kept; countries with no purchases are excluded entirely, so they don't affect the conversion rate, potentially inflating it.

5. Conclude with business implications

Summarize that the choice depends on whether you want to include zero-purchase countries in the analysis. Recommend LEFT JOIN for a complete view and RIGHT JOIN for a purchase-focused view.

Key Points to Mention

  • LEFT JOIN retains all rows from the left table (e.g., all countries), while RIGHT JOIN retains all rows from the right table (e.g., all purchases).
  • Countries with no purchases will appear with NULL values in a LEFT JOIN, which can be coalesced to 0 for conversion rate calculation.
  • Including zero-purchase countries in the denominator lowers the overall conversion rate, providing a more conservative and complete metric.
  • Excluding zero-purchase countries (as in RIGHT JOIN if countries are left) can overstate conversion rate by ignoring non-converting segments.
  • The choice should align with the business question: are we measuring performance across all countries or only active markets?
  • Always check for duplicate rows or fan-out effects that could distort counts when joining.

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