Interview prep · 64 questions

Data analyst interview questions

Questions that come up in real analyst interviews. Answer each one out loud first, then open it: the model answer, the answer that costs points, and a level where you can practise it on real data.

Junior: screening and first jobs. Analyst: the core of most loops. Senior: take-homes and follow-ups that test judgement.

SQL

Asked in almost every analyst loop, often live in a shared editor. Joins, grouping, NULLs and window functions come up most.

What is the difference between WHERE and HAVING?Junior

Model answer. WHERE filters rows before they are grouped; HAVING filters groups after the aggregates are computed. So a condition on a plain column (status = 'completed') goes in WHERE, and a condition on an aggregate (COUNT(*) >= 20) goes in HAVING. Filtering early in WHERE is also cheaper, because fewer rows reach the GROUP BY.

Common wrong answer. "They do the same thing, HAVING is just used with GROUP BY." A HAVING condition on a grouped column still runs, but an aggregate in WHERE is an error, and the interviewer wants to hear why.

What do COUNT(*), COUNT(column) and COUNT(DISTINCT column) return?Junior

Model answer. COUNT(*) counts rows. COUNT(column) counts rows where that column is not NULL. COUNT(DISTINCT column) counts the different non-NULL values. On the customers table, COUNT(*) and COUNT(referral_source) differ by the number of customers with no referral source.

SELECT COUNT(*) AS customers,
       COUNT(referral_source) AS with_source,
       COUNT(DISTINCT referral_source) AS sources
FROM customers;

Common wrong answer. "They are the same unless there are duplicates." The NULL behaviour is the point of the question.

Explain INNER JOIN and LEFT JOIN. What happens to rows with no match?Junior

Model answer. An INNER JOIN keeps only rows that match on both sides. A LEFT JOIN keeps every row of the left table; where nothing matches, the right table's columns come back as NULL. Use a LEFT JOIN when the left side must stay complete, for example every customer, including those who never ordered.

Common wrong answer. Drawing a Venn diagram and stopping there. It hides the fact that one left row can match many right rows and come back several times.

Why can't you use a column alias from SELECT in the WHERE clause?Junior

Model answer. Logically the query runs FROM and JOIN, then WHERE, GROUP BY, HAVING, SELECT, ORDER BY and LIMIT. WHERE runs before SELECT, so the alias does not exist yet. ORDER BY runs after SELECT, which is why the alias works there.

Common wrong answer. "Aliases only work in ORDER BY because of a syntax rule." The run order is the actual reason.

What is the difference between UNION and UNION ALL?Junior

Model answer. Both stack the rows of two queries with the same number and types of columns. UNION removes duplicate rows from the combined result, which costs a sort or hash; UNION ALL keeps every row. Use UNION ALL unless you need duplicates removed, and say why when you do.

SELECT channel AS source FROM marketing_spend
UNION
SELECT source FROM web_sessions
ORDER BY source;

Common wrong answer. "UNION joins two tables side by side." That is a JOIN; UNION stacks rows.

What are DDL and DML? And what is the difference between DELETE, TRUNCATE and DROP?Junior

Model answer. DDL (data definition) changes structure: CREATE, ALTER, DROP. DML (data manipulation) changes rows: INSERT, UPDATE, DELETE. DELETE removes the rows a WHERE clause picks and can be rolled back inside a transaction; TRUNCATE empties the whole table quickly and keeps its structure; DROP removes the table itself, structure included.

Common wrong answer. "DELETE without WHERE is the same as DROP." The table is still there after DELETE, just empty.

Find the customers who have never placed an order.Analyst

Model answer. LEFT JOIN customers to orders and keep the rows where the order side is NULL (an anti-join), or use NOT EXISTS. Test the NULL on the join key or primary key of the right table, which can only be NULL when there was no match.

SELECT c.customer_id, c.email
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;

Common wrong answer. WHERE customer_id NOT IN (SELECT customer_id FROM orders). It works here, but returns nothing at all as soon as the subquery holds one NULL.

Your LEFT JOIN returns fewer rows than the left table has. What happened?Analyst

Model answer. A condition on the right table sits in the WHERE clause. For unmatched rows those columns are NULL, the condition is not true, and the rows are dropped, so the LEFT JOIN behaves like an INNER JOIN. Move the condition into the ON clause to filter what gets joined while keeping every left row.

SELECT c.customer_id, COUNT(o.order_id) AS app_orders
FROM customers AS c
LEFT JOIN orders AS o
  ON o.customer_id = c.customer_id AND o.channel = 'app'
GROUP BY c.customer_id;

Common wrong answer. "Use a RIGHT JOIN instead" or "add DISTINCT". Neither touches the cause.

Return the product(s) with the second-highest price.Analyst

Model answer. Rank prices with DENSE_RANK in descending order and keep rank 2. DENSE_RANK handles ties: if two products share the top price, the next price is still rank 2. Say how you treat ties before you write the query.

WITH ranked AS (
  SELECT name, unit_price,
         DENSE_RANK() OVER (ORDER BY unit_price DESC) AS rnk
  FROM products
)
SELECT name, unit_price
FROM ranked
WHERE rnk = 2;

Common wrong answer. ORDER BY unit_price DESC LIMIT 1 OFFSET 1. With a tie at the top it returns the top price again.

What is the difference between ROW_NUMBER, RANK and DENSE_RANK?Analyst

Model answer. All three number rows in the window order. ROW_NUMBER gives every row a unique number, breaking ties arbitrarily unless you add a tie-breaker. RANK gives tied rows the same number and then skips (1, 2, 2, 4). DENSE_RANK gives ties the same number without gaps (1, 2, 2, 3).

Common wrong answer. "They are the same, just different names." Or using ROW_NUMBER for "top 3 prices" when ties should share a place.

How would you find duplicate customer records?Analyst

Model answer. Decide what makes two records the same person, clean that key (LOWER and TRIM on the email), then GROUP BY the key and keep groups with COUNT(*) > 1. Return the ids in each group so someone can decide which record to keep.

SELECT LOWER(TRIM(email)) AS email_key,
       COUNT(*) AS records,
       MIN(customer_id) AS first_id
FROM customers
GROUP BY 1
HAVING COUNT(*) > 1;

Common wrong answer. SELECT DISTINCT *. Duplicates in real data differ in an id, a timestamp or the casing, so DISTINCT removes nothing.

What is a CTE and why would you use one instead of a subquery?Analyst

Model answer. A CTE (WITH name AS (...)) names an intermediate result you can query like a table. It reads top to bottom, each step can be checked on its own, and it can be referenced more than once in the query. In most engines it performs the same as the equivalent subquery; the gain is readability and fewer mistakes.

Common wrong answer. "CTEs are faster." Sometimes, often not; claiming it without knowing the engine is a red flag.

How do you compute a running total?Analyst

Model answer. SUM(value) OVER (ORDER BY date). With ORDER BY in the window and no frame, the default frame runs from the first row to the current one, peers included. Add PARTITION BY to restart the total per group, for example per year.

Common wrong answer. A correlated subquery that re-sums all earlier rows for every row. It works, but it is slow and hard to read.

What is the difference between an OLTP and an OLAP database?Analyst

Model answer. OLTP systems run the business: many small reads and writes (an order being placed), normalised tables, current data. OLAP systems serve analysis: large scans and aggregates over history, often a star schema in a data warehouse loaded from the OLTP systems. Analysts usually query the OLAP side so reports do not slow down the shop.

Common wrong answer. Describing them as two vendors or two query languages.

Return the three most expensive products in every category.Senior

Model answer. Number the rows inside each category with ROW_NUMBER() OVER (PARTITION BY category ORDER BY unit_price DESC), then filter on that number in an outer query. Window functions are computed after WHERE, so the filter has to sit one level up, in a CTE or subquery.

WITH ranked AS (
  SELECT category, name, unit_price,
         ROW_NUMBER() OVER (PARTITION BY category
                            ORDER BY unit_price DESC, product_id) AS rn
  FROM products
)
SELECT category, name, unit_price
FROM ranked
WHERE rn <= 3
ORDER BY category, rn;

Common wrong answer. GROUP BY category with ORDER BY and LIMIT 3, which returns three rows in total, not three per category.

After joining orders to order_items, total shipping revenue doubled. Why, and how do you fix it?Senior

Model answer. Shipping fee is stored once per order, but the join returns one row per order line, so each fee is repeated for every line (a fan-out). Sum order-level columns at the order grain: aggregate the lines to one row per order first, then join, or sum the fee before joining.

WITH merch AS (
  SELECT order_id, SUM(quantity * unit_price) AS merch
  FROM order_items
  GROUP BY order_id
)
SELECT o.channel,
       SUM(o.shipping_fee) AS shipping,
       ROUND(SUM(m.merch), 2) AS merchandise
FROM orders AS o
JOIN merch AS m ON m.order_id = o.order_id
GROUP BY o.channel;

Common wrong answer. SUM(DISTINCT shipping_fee). It also drops different orders that happen to have the same fee, like every 6.95.

Why can NOT IN (subquery) return no rows at all?Senior

Model answer. If the subquery returns a NULL, x NOT IN (1, 2, NULL) means x <> 1 AND x <> 2 AND x <> NULL. The last comparison is unknown, so the whole condition is never true. Use NOT EXISTS, or filter NULLs out of the subquery.

SELECT e.employee_id, e.first_name
FROM employees AS e
WHERE NOT EXISTS (
  SELECT 1 FROM employees AS r WHERE r.manager_id = e.employee_id
);

Common wrong answer. "Because of a bug in the database." This is standard three-valued logic.

Calculate month-over-month growth in orders.Senior

Model answer. Aggregate to one row per month first, then use LAG to bring the previous month onto the same row and compute (this − previous) / previous. Multiply by 100.0, not 100: in SQL Server and PostgreSQL a whole number divided by a whole number is integer division and rounds the growth to zero.

WITH monthly AS (
  SELECT DATE_TRUNC('month', order_date) AS month, COUNT(*) AS orders
  FROM orders
  GROUP BY 1
)
SELECT month, orders,
       ROUND(100.0 * (orders - LAG(orders) OVER (ORDER BY month))
             / LAG(orders) OVER (ORDER BY month), 1) AS mom_pct
FROM monthly
ORDER BY month;

Common wrong answer. A self-join on month = month − 1 written on raw dates, which breaks across years and at month ends.

pandas

For roles that use Python: selecting, grouping and merging, and the traps that change row counts.

What is the difference between loc and iloc?Junior

Model answer. loc selects by label: index labels and column names, and accepts boolean masks. iloc selects by integer position. Slices differ too: loc includes the end label, iloc excludes the end position like a Python slice.

Common wrong answer. "loc is for rows and iloc is for columns." Both take rows and columns.

What does df["price"] return, and what does df[["price"]] return?Junior

Model answer. Single brackets with one name return a Series (one column, with the index). A list of names returns a DataFrame, even when the list has one name. It matters because methods and merges behave differently on each.

Common wrong answer. "Both return the column." They return different types.

Why does df[df.a > 1 and df.b < 2] raise an error?Junior

Model answer. Python's and asks each side for a single True/False, and a Series of many booleans has no single truth value. Use the element-wise operators & | ~ and put each comparison in brackets, because & binds tighter than >.

products[(products["category"] == "Decor") & (products["unit_price"] < 50)]

Common wrong answer. "Use and with brackets." Brackets alone do not fix it; the operator must be &.

Which chart would you use to show revenue over time, a breakdown by category, a distribution and a relationship between two numbers?Junior

Model answer. A line chart for change over time; a sorted horizontal bar chart for comparing categories; a histogram or box plot for a distribution; a scatter plot (with a trend line if useful) for two numeric variables. In Python: plt.plot, sns.barplot or df.plot.barh, sns.histplot or sns.boxplot, sns.scatterplot or sns.regplot.

monthly = orders.groupby(orders["order_date"].dt.to_period("M")).size()
monthly.tail(12)

Common wrong answer. A pie chart with many slices, or a line chart across categories that have no order.

In a groupby, what is the difference between size() and count()?Analyst

Model answer. size() counts rows in each group, missing values included, and returns one Series. count() counts non-missing values per column, so it can differ column by column. It mirrors COUNT(*) against COUNT(column) in SQL.

g = customers.groupby("segment")
pd.DataFrame({"rows": g.size(), "with_source": g["referral_source"].count()})

Common wrong answer. "size is the number of columns." That is .shape[1] on a DataFrame.

When do you use merge, join and concat?Analyst

Model answer. merge joins two DataFrames on key columns, like a SQL join (how="left", "inner" …). join is a shortcut for joining on the index. concat stacks frames on top of each other (rows) or side by side (columns) without matching keys.

Common wrong answer. Using concat with axis=1 to combine two tables, which pairs rows by index position or label and silently misaligns them.

How do you deal with missing values?Analyst

Model answer. First measure them (isna().sum()) and find out why they are missing. Then decide per column: drop rows when they are few and random, fill with a label for categories ("unknown"), leave them as NaN when an average should skip them, and say what you did in the write-up. Filling with 0 changes means and totals.

customers.isna().sum()

Common wrong answer. "df.dropna()" on the whole frame. It drops every row with any missing value, often most of the data.

How do you find and remove duplicate rows?Analyst

Model answer. duplicated(subset=...) flags repeats of a key, and drop_duplicates(subset=..., keep="first") removes them. Clean the key first (strip, lower) so near-duplicates are caught, and look at the flagged rows before dropping anything.

key = customers["email"].str.strip().str.lower()
customers[key.duplicated(keep=False)]

Common wrong answer. df.drop_duplicates() with no subset, which only removes rows identical in every column, so near-duplicates stay.

Why is df.apply with a Python function slow, and what do you use instead?Analyst

Model answer. apply calls the Python function once per row. Vectorised operations work on the whole column in compiled code: arithmetic on columns, np.where for if/else, pd.cut for bands, .str and .dt methods. Reach for apply only when no vectorised form exists.

np.where(products["unit_price"] >= 200, "premium", "standard")

Common wrong answer. "Use a for loop with iterrows." That is slower still.

What does SettingWithCopyWarning mean?Senior

Model answer. You assigned to something that may be a view of another DataFrame, so pandas cannot promise the original changes or that it does not. Select and assign in one step with .loc[rows, column] = value, or take an explicit .copy() when you mean to work on a separate frame.

Common wrong answer. "Ignore it, it is just a warning." Sometimes the assignment did not happen.

After a left merge, your DataFrame has more rows than before. Why, and how do you guard against it?Senior

Model answer. The right table has repeated keys, so each left row matches several right rows. Check that the key is unique on the side you expect (right["key"].is_unique), and pass validate="many_to_one" so the merge raises an error instead of quietly multiplying rows.

lines = order_items.merge(products[["product_id", "category"]],
                         on="product_id", how="left", validate="many_to_one")
len(lines) == len(order_items)

Common wrong answer. "Call drop_duplicates() after the merge." It hides the symptom and can delete real rows.

Excel

Still the most used tool in business teams. Expect references, lookups, conditional totals and pivots.

What is the difference between A1, $A$1, A$1 and $A1?Junior

Model answer. A1 is relative: both column and row shift when you copy the formula. $A$1 is absolute and never moves. A$1 locks the row only, $A1 the column only. Mixed references let one formula fill a whole table, for example a rate across the top and items down the side.

Common wrong answer. "The $ is for currency." Or locking everything with $ so the formula cannot be filled.

VLOOKUP, INDEX/MATCH or XLOOKUP: which do you use, and why?Junior

Model answer. XLOOKUP when it is available: exact match by default, looks left or right, has a not-found argument. INDEX/MATCH works in every version and does not break when columns are inserted. VLOOKUP only looks to the right, uses a fixed column number, and defaults to approximate match.

Common wrong answer. "VLOOKUP, it is the standard." Without saying FALSE for exact match.

How do you write a condition with more than two outcomes?Junior

Model answer. IFS(test1, value1, test2, value2, …, TRUE, fallback) or nested IFs. Order the tests so the first true one is the right one (for bands, from the lowest bound up), and end with a catch-all. For several conditions at once, put AND() or OR() inside IF.

Common wrong answer. Several separate IF columns added together, which double counts when two tests are true.

What is the difference between SUMIF and SUMIFS?Junior

Model answer. SUMIF takes one condition, and the range to add comes last: SUMIF(range, criterion, sum_range). SUMIFS takes one or more conditions, and the range to add comes first: SUMIFS(sum_range, range1, criterion1, …). Many analysts use SUMIFS everywhere so the order never changes.

Common wrong answer. Mixing up the argument order between the two, which sums the wrong column without any error.

How do you stop people from typing bad values into a shared sheet?Junior

Model answer. Data › Data Validation: allow only a list (a drop-down of channels), whole numbers in a range, dates in a period or a maximum text length, with an input message and a custom error alert. Keep the allowed list in its own range or Excel Table so it can grow, and protect the sheet if needed.

Common wrong answer. "Add a note asking people to be careful." Prevention beats cleaning afterwards.

VLOOKUP returns a value, but it is the wrong one. What is the likely cause?Analyst

Model answer. The fourth argument is missing or TRUE, so VLOOKUP does an approximate match: on unsorted data it returns whichever row it lands on. Pass FALSE (or 0) for an exact match. Other causes: keys stored as text on one side and numbers on the other, or trailing spaces.

Common wrong answer. "Excel is buggy, retype the formula."

When would you use a pivot table instead of formulas?Analyst

Model answer. For exploring and summarising: totals by category and month, quick filters, drill-down into the rows behind a number. Formulas (SUMIFS) are better when the summary must sit in a fixed report layout, feed other calculations, or update without a refresh.

Common wrong answer. "Pivot tables are for people who cannot write formulas." Both have a place; say when each fits.

A column of numbers will not SUM. What do you check?Analyst

Model answer. Numbers stored as text: they sit left-aligned, often with a green triangle, and SUM skips them. Check with ISNUMBER, convert with VALUE, Text to Columns, or multiplying by 1. Also look for stray spaces and non-breaking spaces from exports.

Common wrong answer. "Retype the numbers by hand." It does not scale and hides the cause.

How do you find duplicates in a list of customers?Analyst

Model answer. Add a helper column with =COUNTIF($J$2:$J$41,J2)>1 to flag every copy, or =COUNTIF($J$2:J2,J2)=1 to mark the first occurrence. Conditional formatting shows them visually; Data › Remove Duplicates deletes them, so keep a copy first. Clean the key with TRIM and LOWER before comparing.

Common wrong answer. Sorting and scanning by eye, which misses near-duplicates with different case or spaces.

Why turn a range into an Excel Table (Ctrl+T)?Analyst

Model answer. A Table grows with new rows, carries formulas and formats down automatically, keeps headers visible, and lets formulas use column names (structured references such as Orders[Quantity]) instead of A1 ranges that go stale. PivotTables, slicers and Power Query read it as one named source.

Common wrong answer. "It just adds colours and banded rows."

Power BI and DAX

For roles that build dashboards: the data model, measures, CALCULATE and Power Query.

In Power BI, what is the difference between a measure and a calculated column?Junior

Model answer. A calculated column is computed once per row when the data refreshes and stored in the model, like a new column in the table; use it to slice or filter by (a price band). A measure is computed at query time for whatever filters the visual applies (a total, a rate); use it for numbers you aggregate. Most KPIs should be measures.

Common wrong answer. "They are the same, a measure is just a column with SUM." Calculated columns used for totals bloat the model and ignore the visual's filters.

In Power Query, what is the difference between Merge and Append?Junior

Model answer. Merge joins two queries side by side on a key, like a SQL JOIN or pandas merge (left outer, inner, anti …). Append stacks queries with the same columns on top of each other, like UNION ALL or pd.concat, for example monthly files into one table.

Common wrong answer. Swapping the two, or using Merge to combine twelve monthly files.

What does CALCULATE do in DAX?Analyst

Model answer. CALCULATE evaluates an expression in a modified filter context: it adds, replaces or removes filters before computing. For example, CALCULATE([Revenue], Orders[Channel] = "web") gives web revenue in every cell of a visual, and CALCULATE([Revenue], ALL(Products)) ignores product filters to give a total for a share-of-total.

Common wrong answer. "It is like SUMIF." It changes the filter context for any expression, which is why time intelligence and percent-of-total are built on it.

What is a star schema, and why does Power BI prefer it?Analyst

Model answer. One fact table of events with numbers (order lines: quantity, price, keys) in the middle, joined one-to-many to dimension tables that describe them (products, customers, dates). Filters flow from dimensions to the fact table, measures stay simple, and the engine compresses it well. One big flat table or a web of tables both make DAX harder and slower.

Common wrong answer. "Put everything in one table so no relationships are needed."

Why do you need a separate date table for year-over-year in Power BI?Analyst

Model answer. Time-intelligence functions such as SAMEPERIODLASTYEAR, DATESYTD and TOTALYTD need a continuous calendar with one row per day, marked as the date table and related to the fact table's date. Dates with no sales still need a row, otherwise the previous-year comparison has gaps.

Common wrong answer. "Use the order date column directly." It skips days without orders and breaks time intelligence.

How would you make each regional manager see only their region in one Power BI report?Analyst

Model answer. Row-level security: define a role with a DAX filter on the region dimension (static, or dynamic with USERPRINCIPALNAME() matched to a user-to-region table), publish to the Power BI Service and assign users or groups to the role. Test with "View as" before sharing.

Common wrong answer. "Make a copy of the report per region." It multiplies maintenance and still shares the full dataset.

Import or DirectQuery: how do you choose?Senior

Model answer. Import loads a compressed copy into the model: fastest visuals and full DAX, but data is only as fresh as the last refresh and size is limited. DirectQuery sends queries to the source each time: near-real-time and no size limit, but slower visuals, more load on the source and some DAX limits. Default to Import; choose DirectQuery when freshness or size demands it.

Common wrong answer. "DirectQuery is always better because it is live."

Statistics

Enough to read an experiment and avoid the classic misreadings: averages, p-values, intervals, error types.

When would you report the median instead of the mean?Junior

Model answer. When the data is skewed or has outliers: order values, salaries, time on site. A few very large business orders pull the mean up, while the median shows the typical order. Report both when the gap between them is itself the story.

lines = order_items.assign(rev=order_items["quantity"] * order_items["unit_price"])
order_value = lines.groupby("order_id")["rev"].sum()
order_value.agg(["mean", "median"]).round(2)

Common wrong answer. "The median is more accurate." Neither is more accurate; they answer different questions.

Sessions from email convert better than paid sessions. Does email cause more sales?Junior

Model answer. Not necessarily. Email reaches people who already know the store, so the audience differs (selection). Correlation shows the two move together; causation needs a comparison where the only difference is the email, ideally a randomised test with a holdout group.

SELECT source,
       ROUND(AVG(CASE WHEN converted THEN 1.0 ELSE 0 END) * 100, 2) AS conversion_pct
FROM web_sessions
GROUP BY source
ORDER BY conversion_pct DESC;

Common wrong answer. "Yes, so we should move the paid budget into email."

How do you find outliers, and what do you do with them?Junior

Model answer. Look first (box plot, histogram), then use a rule such as values beyond 1.5 × IQR from the quartiles. Then investigate: a data error gets fixed or removed, a real but rare value (a 100-unit business order) stays, and you may report results with and without it. Never drop outliers silently.

q1, q3 = order_items["quantity"].quantile([0.25, 0.75])
iqr = q3 - q1
order_items[(order_items["quantity"] < q1 - 1.5 * iqr) | (order_items["quantity"] > q3 + 1.5 * iqr)]

Common wrong answer. "Remove anything more than 3 standard deviations away" as an automatic step.

What is a p-value?Analyst

Model answer. Assuming there is no real effect (the null hypothesis), the p-value is the probability of seeing a result at least as extreme as the one observed. A small p-value means the data would be surprising if there were no effect. It is not the size of the effect and not the chance the result is real.

Common wrong answer. "The probability that the null hypothesis is true", or "the probability the result happened by chance". Both are the classic mistake.

What does a 95% confidence interval mean?Analyst

Model answer. If you repeated the sampling many times and built an interval the same way each time, about 95% of those intervals would contain the true value. It expresses how precise the estimate is: a wide interval means you know little.

Common wrong answer. "There is a 95% probability that the true value is inside this interval." The true value is fixed; the interval is what varies.

What are Type I and Type II errors?Analyst

Model answer. A Type I error is a false positive: you conclude there is an effect when there is none (its rate is alpha, often 5%). A Type II error is a false negative: you miss an effect that is there (its rate is beta; power = 1 − beta). Lowering one usually raises the other unless you collect more data.

Common wrong answer. Swapping the two, or saying a larger sample reduces only Type I errors.

How would you set up an A/B test of a new checkout page?Analyst

Model answer. Pick one primary metric (checkout conversion) and a few guardrails (order value, errors). Decide the smallest effect worth detecting and compute the sample size and duration before starting. Randomise users, not sessions, run full weeks, do not stop early when it looks good, then compare with a significance test and a confidence interval.

Common wrong answer. "Run it until the result is significant." Peeking inflates false positives.

What is the difference between standard deviation and standard error?Analyst

Model answer. Standard deviation measures how spread out individual values are. Standard error measures how precise an estimate (usually a mean) is: SD divided by the square root of the sample size. More data shrinks the standard error, not the standard deviation.

Common wrong answer. "They are the same thing", or using SD for error bars on a mean.

What is Simpson's paradox? Give an example.Senior

Model answer. A trend that appears in every group reverses or disappears when the groups are combined, because the groups have different sizes and different base rates. A made-up example: the app converts better than the web on both mobile and desktop, yet worse overall, because most app traffic is mobile, where everything converts less. Always check the mix behind an overall rate.

Common wrong answer. Describing it as "a rare statistical curiosity". It shows up in channel and segment reports all the time.

Case and product sense

Open questions about a business. The interviewer is scoring your structure and checks, not one right number.

Your manager says "we want more sales". How do you turn that into something you can analyse?Junior

Model answer. Make it a SMART goal: specific (which product, channel or customers), measurable (which metric and baseline), achievable (based on past growth), relevant (tied to a business priority) and time-bound (by when). For example: grow repeat orders from app customers by 10% this quarter compared with last. Then agree the metric definition before pulling data.

Common wrong answer. Starting to pull every sales table and building charts before the question is pinned down.

Which metrics would you put on a weekly dashboard for an online store?Junior

Model answer. Revenue, broken into orders × average order value; conversion rate (orders ÷ sessions); new vs returning customers; return or refund rate; and marketing cost per order. Each with the same week last year for context, and one owner per metric. Fewer metrics, clearly defined, beat a wall of charts.

Common wrong answer. Listing twenty metrics with no definitions and no comparison point.

Revenue fell 10% last week. How do you investigate?Analyst

Model answer. First check the data: is the week complete, did tracking or a pipeline change? Then split revenue into orders × average order value and see which moved. Break the one that moved down by channel, device, category, new vs returning customers, and compare with the same week last year for seasonality. Report the biggest driver with a number, and what you ruled out.

Common wrong answer. Jumping to a cause ("the price change") before checking the data and decomposing the metric.

How would you measure customer retention for an online store?Analyst

Model answer. Group customers into cohorts by the month of their first order, then measure the share who order again within a set window (say 90 days), or in each later month. Only count customers who have had the full window to come back (right-censoring), and compare cohorts rather than one overall number.

Common wrong answer. "Repeat customers divided by all customers", mixing customers who joined last week with customers from three years ago.

Conversion rate went up, but revenue went down. How can both be true?Analyst

Model answer. Fewer sessions (a traffic drop) with a better rate can still mean fewer orders; or more orders at a lower average order value (discounts, cheaper mix); or more cancellations and refunds after purchase. Decompose revenue into sessions × conversion × AOV × (1 − refunds) and check which term moved.

Common wrong answer. "The data must be wrong." Check it, but this is a normal mix effect.

How do you present a finding to a manager who does not read SQL?Analyst

Model answer. Lead with the answer and the number ("Returns cost us $X in Q3, most of it Furniture damaged in transit"), then the one chart that shows it, then what you recommend and how sure you are. Methods and caveats go after, briefly. Use their words: orders and customers, not rows and joins.

Common wrong answer. Walking through the queries in the order you wrote them, ending with the result.

How do you check your numbers before you send them?Analyst

Model answer. Know the grain of every table you join and check row counts before and after each join. Reconcile one total against a trusted source (finance revenue, a dashboard). Spot-check a few records end to end, look for NULLs and duplicates in the keys, and make sure filters such as status and dates match the question.

Common wrong answer. "I trust the query if it runs without errors."

Marketing asks which channel is the most efficient. What do you compute?Senior

Model answer. Cost per conversion (spend ÷ converted sessions) per channel over the same period, alongside conversion rate and the value of those orders, so cheap but low-value conversions do not win by default. Aggregate spend and sessions separately before joining them, or the join multiplies spend.

WITH spend AS (
  SELECT channel, SUM(amount) AS spend
  FROM marketing_spend
  GROUP BY channel
), conv AS (
  SELECT source AS channel, COUNT(*) FILTER (WHERE converted) AS conversions
  FROM web_sessions
  GROUP BY source
)
SELECT s.channel, s.spend, c.conversions,
       ROUND(s.spend / NULLIF(c.conversions, 0), 2) AS cost_per_conversion
FROM spend AS s
LEFT JOIN conv AS c ON c.channel = s.channel;

Common wrong answer. Ranking channels by total conversions, which rewards the channel with the biggest budget.

You are comparing this holiday season with last year. What can go wrong?Senior

Model answer. Calendars shift: Black Friday falls on a different date, and the period may not have the same number of weekends. Align by event (Black Friday to Cyber Monday) or by weekday, make sure both years are complete, and keep status rules the same (completed orders only, or net of refunds) in both years.

Common wrong answer. Comparing 1–30 November across years without checking where Black Friday falls.

Before the interview

Cheat sheet

SQL cheat sheet

One page to revise the SQL you will be asked about, printable.

Cheat sheet

pandas cheat sheet

One page to revise the pandas you will be asked about, printable.

Cheat sheet

Excel cheat sheet

One page to revise the Excel you will be asked about, printable.

Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.