Skip to content

Practical Data Analytics Interview Questions

90 questions with answers. Hands-on questions where you write code, a query, a formula or concrete steps.

How to approach these questions

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.

Solved — practicalStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

How do you merge two DataFrames on different column names?

Save to ▾
Show answer
pd.merge(df1, df2, left_on='cust_id', right_on='id', how='left') joins on differently named keys.
02

How do you calculate a running total?

SQLMedium
Save to ▾
Show answer
SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) adds up values cumulatively.
03

How do you remove duplicates in Excel but keep the latest record?

ExcelMedium
Save to ▾
Show answer
Sort by date (newest first), then Data → Remove Duplicates on the key column — Excel keeps the first occurrence. Or use UNIQUE / a COUNTIFS helper column.
04

How do you handle outliers in Pandas?

Save to ▾
Show answer
Detect with IQR or z-score, then cap with clip, remove, transform with log, or keep and flag them depending on whether they are errors or real events.
05

How do you read a CSV and inspect it in Pandas?

Save to ▾
Show answer
df = pd.read_csv('file.csv'); then head(), info(), describe() and isna().sum() for a quick health check.
06

Explain INDEX and MATCH together.

ExcelMedium
Save to ▾
Show answer
MATCH finds the position of a value in a range and INDEX returns the item at that position, giving a flexible lookup that works left or right and survives column insertions.
07

SUMIFS vs COUNTIFS?

ExcelEasy
Save to ▾
Show answer
SUMIFS adds values that meet several conditions; COUNTIFS counts rows that meet several conditions. Example: =SUMIFS(Sales, Region, "North", Month, "Jan")
08

Explain GROUP BY with an example.

SQLMedium
Save to ▾
Show answer
It groups rows sharing values so aggregates run per group, for example SELECT region, SUM(sales) FROM orders GROUP BY region.
09

How do you create a new column from conditions?

Save to ▾
Show answer
Use np.where for one condition, np.select for several, or df.apply with a function for custom logic.
10

How do you calculate month-over-month growth?

Save to ▾
Show answer
Aggregate revenue by month, use LAG(revenue) OVER (ORDER BY month), then (revenue - prev) / prev x 100.
11

How do you create a dynamic dashboard in Excel?

ExcelMedium
Save to ▾
Show answer
Build PivotTables from a clean table, add PivotCharts and slicers, link KPIs with formulas, and refresh from Power Query so everything updates together.
12

How do you handle errors like #N/A and #DIV/0!?

ExcelMedium
Save to ▾
Show answer
Fix the root cause when possible, or wrap formulas with IFERROR or IFNA to return a clear fallback value.
13

Write SQL for a running total of sales by date.

SQLMedium
Save to ▾
Show answer
SELECT order_date, SUM(amount) OVER (ORDER BY order_date) AS running_total FROM sales;
14

Find the second highest salary in SQL.

SQLEasy
Save to ▾
Show answer
SELECT MAX(salary) FROM emp WHERE salary < (SELECT MAX(salary) FROM emp); -- or: DENSE_RANK() OVER (ORDER BY salary DESC) = 2
15

How do you change data types and parse dates?

Save to ▾
Show answer
df['date'] = pd.to_datetime(df['date']); df['amount'] = pd.to_numeric(df['amount'], errors='coerce'); astype('category') for repeated strings.
16

How do you connect Python to a SQL database?

Save to ▾
Show answer
Create an engine with SQLAlchemy or a driver such as psycopg2, then run pd.read_sql(query, engine) to load results into a DataFrame.
17

How do you clean messy text data in Excel?

Save to ▾
Show answer
Use TRIM, CLEAN, UPPER or PROPER, Text to Columns, Flash Fill, SUBSTITUTE, and Power Query for repeatable cleaning steps.
18

Show a Pandas groupby example.

Save to ▾
Show answer
df.groupby('region')['sales'].agg(['sum', 'mean', 'count']) # splits by region, aggregates, combines the results
19

How do you calculate a rolling average?

SQLMedium
Save to ▾
Show answer
df['sales'].rolling(window=7).mean() gives a 7-period moving average, useful to smooth daily noise.
20

How do you find duplicate rows in a table?

SQLMedium
Save to ▾
Show answer
SELECT col, COUNT(*) FROM t GROUP BY col HAVING COUNT(*) > 1; use ROW_NUMBER() partitioned by the key to delete extras.
21

How would you find customers who never placed an order?

SQLMedium
Save to ▾
Show answer
LEFT JOIN customers to orders and keep rows where order_id IS NULL, or use NOT EXISTS, which also handles NULLs safely.
22

Find customers who ordered in January but not in February.

SQLHard
Save to ▾
Show answer
SELECT DISTINCT customer_id FROM orders WHERE month = 'Jan' EXCEPT SELECT customer_id FROM orders WHERE month = 'Feb';
23

What does the IF function do and how do you nest it?

ExcelEasy
Save to ▾
Show answer
IF returns one result if a test is true and another if false. Nest IFs for multiple outcomes, or use IFS for cleaner formulas.
24

How do you remove duplicates in Excel?

ExcelEasy
Save to ▾
Show answer
Data tab, Remove Duplicates, pick the columns to compare. For a non-destructive way use UNIQUE() or conditional formatting to highlight repeats first.
25

How does the FILTER function work in Excel? Write a formula to list all North region orders.

ExcelMedium
Save to ▾
Show answer
-- A2:D500 holds Order, Customer, Region, Amount =FILTER(A2:D500, C2:C500="North", "No orders") -- two conditions (AND) multiply the tests =FILTER(A2:D500, (C2:C500="North")*(D2:D500>10000), "No orders") -- OR conditions add the tests =FILTER(A2:D500, (C2:C500="North")+(C2:C500="East"))
26

Write Excel formulas to get a sorted list of unique customer names and their count.

ExcelEasy
Save to ▾
Show answer
-- unique customers from column B, sorted A to Z =SORT(UNIQUE(B2:B1000)) -- number of unique customers =COUNTA(UNIQUE(B2:B1000)) -- unique customers sorted by total sales, highest first =SORTBY(UNIQUE(B2:B1000), SUMIFS(D2:D1000, B2:B1000, UNIQUE(B2:B1000)), -1)
27

Write an Excel formula to put invoices into ageing buckets (0-30, 31-60, 61-90, 90+ days).

ExcelMedium
Save to ▾
Show answer
-- C2 = invoice date, TODAY() = as-of date =LET(age, TODAY()-C2, IFS(age<=30, "0-30", age<=60, "31-60", age<=90, "61-90", TRUE, "90+")) -- then a PivotTable with bucket in rows and SUM of amount gives the ageing report
28

How do you combine text from several cells with a separator in Excel?

ExcelEasy
Save to ▾
Show answer
-- join first and last name with a space =TEXTJOIN(" ", TRUE, A2, B2) -- all products of one order in a single cell, comma separated =TEXTJOIN(", ", TRUE, FILTER(C2:C500, A2:A500=F2)) -- older method =A2 & " " & B2 -- TRUE in TEXTJOIN skips empty cells, which CONCAT and & do not
29

How do you calculate working days between two dates in Excel, excluding holidays?

ExcelEasy
Save to ▾
Show answer
-- A2 = start date, B2 = end date, Holidays = named range of holiday dates =NETWORKDAYS(A2, B2, Holidays) -- if the weekend is only Sunday (weekend code 11) =NETWORKDAYS.INTL(A2, B2, 11, Holidays) -- date that is 10 working days after A2 =WORKDAY(A2, 10, Holidays)
30

Write a DAX measure for year-over-year sales growth percentage.

Power BIMedium
Save to ▾
Show answer
Sales LY = CALCULATE([Total Sales], SAMEPERIODLASTYEAR('Date'[Date])) Sales YoY % = VAR cur = [Total Sales] VAR prev = [Sales LY] RETURN DIVIDE(cur - prev, prev) -- needs a marked date table; format the measure as a percentage
31

Write a DAX measure for cumulative sales from the start of the data to the selected date.

Power BIMedium
Save to ▾
Show answer
Cumulative Sales = VAR maxDate = MAX('Date'[Date]) RETURN CALCULATE( [Total Sales], FILTER(ALL('Date'[Date]), 'Date'[Date] <= maxDate) ) -- for a running total that resets each year use TOTALYTD([Total Sales], 'Date'[Date])
32

Your Sales table has OrderDate and ShipDate. How do you analyse sales by ship date when only OrderDate is the active relationship?

Save to ▾
Show answer
-- create an inactive relationship Sales[ShipDate] -> 'Date'[Date] in the model Sales by Ship Date = CALCULATE( [Total Sales], USERELATIONSHIP(Sales[ShipDate], 'Date'[Date]) ) -- the measure uses the inactive relationship only inside this CALCULATE -- the alternative is a second role-playing date table
33

Write pandas code to calculate monthly revenue from an orders DataFrame.

Save to ▾
Show answer
import pandas as pd orders = pd.read_csv('orders.csv', parse_dates=['order_date']) orders['revenue'] = orders['qty'] * orders['unit_price'] monthly = ( orders .groupby(orders['order_date'].dt.to_period('M'))['revenue'] .sum() .reset_index(name='revenue') ) print(monthly)
34

Write pandas code for a pivot table of sales by region and category with sum and count.

Save to ▾
Show answer
import pandas as pd summary = pd.pivot_table( df, index='region', columns='category', values='sales', aggfunc=['sum', 'count'], fill_value=0, margins=True # adds an All row and column ) print(summary)
35

Write pandas code to compute month-on-month percentage change and cumulative sales for each store.

Save to ▾
Show answer
df = df.sort_values(['store', 'month']) df['mom_pct'] = ( df.groupby('store')['sales'].pct_change() * 100 ).round(1) df['cum_sales'] = df.groupby('store')['sales'].cumsum() # first month of each store has NaN for mom_pct, which is expected
36

Write pandas code to extract month name, weekday and hour from a timestamp column.

Save to ▾
Show answer
df['ts'] = pd.to_datetime(df['ts'], errors='coerce') df['month'] = df['ts'].dt.month_name() df['weekday'] = df['ts'].dt.day_name() df['hour'] = df['ts'].dt.hour df['is_weekend'] = df['ts'].dt.dayofweek >= 5 # orders by weekday print(df['weekday'].value_counts())
37

Write Python code to fit a simple linear regression of sales on ad spend.

Save to ▾
Show answer
import pandas as pd import statsmodels.formula.api as smf df = pd.read_csv('marketing.csv') model = smf.ols('sales ~ ad_spend', data=df).fit() print(model.summary()) # coefficients, p-values, R-squared # predicted sales for a spend of 50,000 print(model.predict(pd.DataFrame({'ad_spend': [50000]})))
38

Write a Python function that reads all Excel files in a folder and combines them into one DataFrame.

Save to ▾
Show answer
from pathlib import Path import pandas as pd def combine_excel(folder): frames = [] for f in Path(folder).glob('*.xlsx'): df = pd.read_excel(f) df['source_file'] = f.name frames.append(df) return pd.concat(frames, ignore_index=True) all_sales = combine_excel('monthly_reports')
39

Write SQL using LAG to compare each month's revenue with the previous month.

SQLMedium
Save to ▾
Show answer
WITH monthly AS ( SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount) AS revenue FROM orders GROUP BY 1 ) SELECT month, revenue, LAG(revenue) OVER (ORDER BY month) AS prev_revenue, ROUND(100.0 * (revenue - LAG(revenue) OVER (ORDER BY month)) / NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 1) AS mom_pct FROM monthly ORDER BY month;
40

Write SQL to find the top 3 products by revenue within each category.

SQLHard
Save to ▾
Show answer
WITH ranked AS ( SELECT category, product, SUM(amount) AS revenue, DENSE_RANK() OVER ( PARTITION BY category ORDER BY SUM(amount) DESC ) AS rnk FROM sales GROUP BY category, product ) SELECT category, product, revenue FROM ranked WHERE rnk <= 3 ORDER BY category, rnk;
41

Write SQL to show each category's percentage share of total revenue.

SQLMedium
Save to ▾
Show answer
SELECT category, SUM(amount) AS revenue, ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 2) AS pct_share FROM sales GROUP BY category ORDER BY revenue DESC; -- SUM(SUM(amount)) OVER () is the grand total across all groups
42

How do you pivot rows into columns in SQL, for example sales by quarter as columns?

SQLMedium
Save to ▾
Show answer
SELECT region, SUM(CASE WHEN quarter = 'Q1' THEN amount ELSE 0 END) AS q1, SUM(CASE WHEN quarter = 'Q2' THEN amount ELSE 0 END) AS q2, SUM(CASE WHEN quarter = 'Q3' THEN amount ELSE 0 END) AS q3, SUM(CASE WHEN quarter = 'Q4' THEN amount ELSE 0 END) AS q4 FROM sales GROUP BY region; -- conditional aggregation works in every database; some also offer PIVOT
43

Write SQL to find users who logged in on at least 3 consecutive days.

SQLHard
Save to ▾
Show answer
WITH d AS ( SELECT DISTINCT user_id, CAST(login_time AS DATE) AS dt FROM logins ), g AS ( SELECT user_id, dt, dt - CAST(ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY dt) AS INT) AS grp FROM d ) SELECT DISTINCT user_id FROM g GROUP BY user_id, grp HAVING COUNT(*) >= 3; -- gaps and islands: consecutive dates share the same grp value
44

Write SQL to count orders by week and by day of week.

SQLEasy
Save to ▾
Show answer
-- orders per week (PostgreSQL style) SELECT DATE_TRUNC('week', order_date) AS week_start, COUNT(*) AS orders FROM orders GROUP BY 1 ORDER BY 1; -- orders by day of week (0 = Sunday) SELECT EXTRACT(DOW FROM order_date) AS dow, COUNT(*) AS orders FROM orders GROUP BY 1 ORDER BY 1;
45

How do you calculate a simple customer lifetime value?

Save to ▾
Show answer
A simple formula is CLV = average order value x purchase frequency per year x gross margin x expected lifetime in years. Example: 800 rupees x 6 orders x 30 percent margin x 3 years = 4,320 rupees. Expected lifetime can be estimated as 1 divided by annual churn rate. A better version discounts future value and calculates CLV by cohort or segment, because averages hide very different customer groups.
46

Write a Tableau calculation to find each customer's first purchase date and flag new versus repeat orders.

TableauMedium
Save to ▾
Show answer
// First Purchase Date { FIXED [Customer ID] : MIN([Order Date]) } // Order Type IF [Order Date] = [First Purchase Date] THEN 'New' ELSE 'Repeat' END // Cohort Month for retention analysis DATETRUNC('month', [First Purchase Date])
47

How do you create a dual-axis chart in Tableau, for example sales bars with a profit margin line?

Save to ▾
Show answer
Drag Order Date to Columns as a continuous or discrete month, put Sales on Rows, then drag Profit Ratio to Rows next to it. Right-click the second pill and choose Dual Axis. On the Marks card set Sales to Bar and Profit Ratio to Line. Decide whether to synchronise axes: do not synchronise when units differ, like rupees and percentage. Label the axes clearly so readers know which is which.
48

Write a Tableau calculated field for profit ratio and a profit category.

Save to ▾
Show answer
// Profit Ratio (aggregate first, then divide) SUM([Profit]) / SUM([Sales]) // Profit Category IF SUM([Profit]) < 0 THEN 'Loss' ELSEIF [Profit Ratio] < 0.1 THEN 'Low margin' ELSE 'Healthy' END // avoid AVG([Profit]/[Sales]): it averages row ratios and gives a wrong total
49

Write a Tableau table calculation for year-over-year sales growth.

TableauMedium
Save to ▾
Show answer
// with YEAR(Order Date) in the view, Compute Using: Table (across) (ZN(SUM([Sales])) - LOOKUP(ZN(SUM([Sales])), -1)) / ABS(LOOKUP(ZN(SUM([Sales])), -1)) // the built-in Quick Table Calculation 'Year over Year Growth' generates similar logic // format the field as a percentage
50

How do you let users choose Top N customers with a parameter in Tableau?

TableauMedium
Save to ▾
Show answer
Create an integer parameter Top N with a range of 5 to 50 and show its control. Drag Customer to the Filters shelf, choose the Top tab, select By field, and set it to top Top N by SUM(Sales). Alternatively create a set based on the parameter so you can show Top N versus Others in one chart. If the Top N must respect a category filter, add that category filter to context.
51

How do you handle null values in Tableau calculations?

Save to ▾
Show answer
// treat missing sales as zero ZN(SUM([Sales])) // replace a missing value with a default IFNULL([Region], 'Unknown') // test explicitly IF ISNULL([Discount]) THEN 0 ELSE [Discount] END // right-click a null indicator on an axis to filter or show nulls at default position
52

Numbers in an Excel column are stored as text and SUM returns zero. How do you fix it?

Save to ▾
Show answer
Signs are left-aligned numbers, green triangles, and COUNT returning less than COUNTA. Fix options: select the column and use the warning menu Convert to Number; use Data, Text to Columns and Finish; or a helper column with =VALUE(TRIM(A2)). If there are hidden characters, use =VALUE(SUBSTITUTE(TRIM(A2), CHAR(160), "")) to remove non-breaking spaces from web exports. In Power Query, simply change the column type to number.
53

Write pandas code to standardise inconsistent city names such as 'delhi ', 'New Delhi' and 'DELHI'.

Save to ▾
Show answer
city_map = { 'delhi': 'Delhi', 'new delhi': 'Delhi', 'bombay': 'Mumbai', 'mumbai': 'Mumbai', 'bengaluru': 'Bengaluru', 'bangalore': 'Bengaluru', } clean = df['city'].str.strip().str.lower().str.replace(r'\s+', ' ', regex=True) df['city_clean'] = clean.map(city_map).fillna(clean.str.title()) print(df['city_clean'].value_counts())
54

Write pandas code to trim whitespace and lower-case every text column in a DataFrame.

Save to ▾
Show answer
text_cols = df.select_dtypes(include='object').columns for col in text_cols: df[col] = ( df[col] .astype('string') .str.strip() .str.lower() .replace('', pd.NA) ) # also tidy column names df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
55

Write SQL to flag rows that fail basic validation rules in a customer table.

Save to ▾
Show answer
SELECT customer_id, CASE WHEN email IS NULL OR email NOT LIKE '%_@_%._%' THEN 1 ELSE 0 END AS bad_email, CASE WHEN LENGTH(pincode) <> 6 THEN 1 ELSE 0 END AS bad_pincode, CASE WHEN signup_date > CURRENT_DATE THEN 1 ELSE 0 END AS future_signup, CASE WHEN age NOT BETWEEN 18 AND 100 THEN 1 ELSE 0 END AS bad_age FROM customers; -- sum each flag to report the error rate per rule
56

How do you unpivot a wide monthly sales sheet into a long table in Power Query?

Save to ▾
Show answer
Load the table into Power Query. Select the identifier columns such as Region and Product, then choose Transform, Unpivot Other Columns. The month columns become two columns, Attribute and Value; rename them Month and Sales. Change Month to a date type and Sales to a decimal number. Using Unpivot Other Columns rather than Unpivot Columns means new months added later are picked up automatically on refresh.
57

What steps would you apply in Power Query to clean a messy system export?

Save to ▾
Show answer
Typical steps: remove top junk rows and promote the real header row, remove blank rows and columns, set correct data types, trim and clean text, split combined columns such as Name - City, replace error values, fill down for grouped labels, remove duplicates on the business key, and unpivot wide month columns. Rename steps meaningfully so others can follow them, and keep the query foldable when the source is a database.
58

Write Python code to extract valid 10-digit Indian mobile numbers from a messy text column.

Save to ▾
Show answer
import re pattern = re.compile(r'(?:\+?91[\s-]?|0)?([6-9]\d{9})') def get_mobile(text): if not isinstance(text, str): return None digits = re.sub(r'[\s()-]', '', text) m = pattern.search(digits) return m.group(1) if m else None df['mobile'] = df['contact'].apply(get_mobile)
59

How do you split a full name column into first and last name in Excel?

Save to ▾
Show answer
-- A2 = 'Priya Ramesh Iyer' -- first name =TEXTBEFORE(TRIM(A2), " ") -- last name (text after the last space) =TEXTAFTER(TRIM(A2), " ", -1) -- older Excel: first name =LEFT(TRIM(A2), FIND(" ", TRIM(A2)&" ")-1) -- or use Data > Text to Columns or Flash Fill (Ctrl+E) for a one-off task
60

How do you show actual versus target on a dashboard?

Save to ▾
Show answer
Use a KPI card with actual, target and variance as both value and percentage, coloured by status. For trends, a bar or line for actual with a target line or band works well. Bullet charts compare actual against target and ranges compactly in tables. Make the period clear, such as month to date versus prorated monthly target, because comparing half a month of sales to a full month target looks falsely bad.
61

Write SQL to calculate monthly customer churn rate.

Save to ▾
Show answer
WITH active AS ( SELECT DISTINCT customer_id, DATE_TRUNC('month', order_date) AS month FROM orders ) SELECT a.month, COUNT(*) AS active_customers, COUNT(*) - COUNT(n.customer_id) AS churned_next_month, ROUND(100.0 * (COUNT(*) - COUNT(n.customer_id)) / COUNT(*), 1) AS churn_pct FROM active a LEFT JOIN active n ON n.customer_id = a.customer_id AND n.month = a.month + INTERVAL '1 month' GROUP BY a.month ORDER BY a.month;
62

Write a DAX measure for a KPI card showing achievement against target and a colour for conditional formatting.

Save to ▾
Show answer
Achievement % = DIVIDE([Total Sales], [Sales Target]) KPI Colour = VAR a = [Achievement %] RETURN SWITCH( TRUE(), ISBLANK(a), "#9E9E9E", a >= 1, "#2E7D32", a >= 0.9, "#F9A825", "#C62828" ) -- apply KPI Colour via Format > Callout value > fx > Field value
63

What questions do you ask a stakeholder before building a dashboard?

Save to ▾
Show answer
Who will use it and what decisions will they make with it? Which three to five questions must it answer? What are the KPI definitions, targets and comparison periods? Which data sources are trusted? How often will they look at it, and on what device? Who needs access and are there security restrictions? What does success look like in a month? I summarise the answers in a short brief and get sign-off before building.
64

How would you write a short email summarising an analysis for a manager?

Save to ▾
Show answer
Use a clear subject that states the finding, such as Q3 repeat purchase rate fell 4 points, mainly in Tier 2 cities. Open with one or two sentences of the key insight and the recommendation. Follow with three bullet-style lines of supporting evidence with numbers, then any caveats about data, and a clear next step or decision needed with a date. Attach or link the detailed workings instead of pasting large tables.
65

Write SQL to get each order's customer city as it was on the order date, using an SCD Type 2 customer dimension.

Save to ▾
Show answer
-- dim_customer has valid_from, valid_to (NULL for current row) SELECT o.order_id, o.order_date, c.customer_id, c.city FROM fact_orders o JOIN dim_customer c ON c.customer_id = o.customer_id AND o.order_date >= c.valid_from AND o.order_date < COALESCE(c.valid_to, DATE '9999-12-31');
66

Write SQL to generate a simple date dimension for 2026.

Save to ▾
Show answer
-- PostgreSQL syntax CREATE TABLE dim_date AS SELECT TO_CHAR(d, 'YYYYMMDD')::INT AS date_key, d::DATE AS full_date, EXTRACT(YEAR FROM d) AS year, EXTRACT(QUARTER FROM d) AS quarter, EXTRACT(MONTH FROM d) AS month_no, TO_CHAR(d, 'Mon') AS month_name, TO_CHAR(d, 'Dy') AS weekday, EXTRACT(ISODOW FROM d) IN (6, 7) AS is_weekend FROM GENERATE_SERIES(DATE '2026-01-01', DATE '2026-12-31', INTERVAL '1 day') AS d;
67

Write a CREATE TABLE statement for a sales fact table linked to date, product and store dimensions.

Save to ▾
Show answer
CREATE TABLE fact_sales ( date_key INT NOT NULL REFERENCES dim_date(date_key), product_key INT NOT NULL REFERENCES dim_product(product_key), store_key INT NOT NULL REFERENCES dim_store(store_key), invoice_no VARCHAR(20) NOT NULL, -- degenerate dimension quantity INT NOT NULL, net_amount DECIMAL(12,2) NOT NULL, discount_amount DECIMAL(12,2) DEFAULT 0 );
68

Write SQL to incrementally load new and changed products into a dimension using MERGE.

Save to ▾
Show answer
MERGE INTO dim_product t USING stg_product s ON t.product_id = s.product_id WHEN MATCHED AND (t.name <> s.name OR t.category <> s.category) THEN UPDATE SET name = s.name, category = s.category, updated_at = CURRENT_TIMESTAMP WHEN NOT MATCHED THEN INSERT (product_id, name, category, updated_at) VALUES (s.product_id, s.name, s.category, CURRENT_TIMESTAMP);
69

Write an Excel formula to look up a price using two criteria, product and city.

ExcelMedium
Save to ▾
Show answer
=XLOOKUP(1, (A2:A500=G2) * (B2:B500=H2), C2:C500, "Not found") -- A = product, B = city, C = price -- G2 and H2 hold the product and city to find -- each condition returns TRUE/FALSE; multiplying gives 1 only where both match
70

Write a DAX measure that counts new customers, meaning customers who bought in the selected period but never before it.

Save to ▾
Show answer
New Customers = VAR PeriodStart = MIN ( 'Date'[Date] ) VAR CurrentCustomers = VALUES ( Sales[CustomerID] ) VAR PastCustomers = CALCULATETABLE ( VALUES ( Sales[CustomerID] ), 'Date'[Date] < PeriodStart, REMOVEFILTERS ( 'Date' ) ) RETURN COUNTROWS ( EXCEPT ( CurrentCustomers, PastCustomers ) )
71

Write pandas code to find the top 2 products by revenue in each region.

Save to ▾
Show answer
top2 = ( df.groupby(['region', 'product'], as_index=False)['revenue'] .sum() .sort_values(['region', 'revenue'], ascending=[True, False]) .groupby('region') .head(2) ) print(top2)
72

Write SQL to find the median order value overall and per city.

SQLMedium
Save to ▾
Show answer
-- Works in PostgreSQL, Snowflake, SQL Server (with OVER for SQL Server) SELECT city, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_value) AS median_value, AVG(order_value) AS mean_value FROM orders GROUP BY city ORDER BY median_value DESC;
73

Write Python code to calculate the sample size per group for a conversion test with a 4% baseline and a 10% relative lift.

Save to ▾
Show answer
from statsmodels.stats.power import NormalIndPower from statsmodels.stats.proportion import proportion_effectsize baseline = 0.04 target = baseline * 1.10 # 4.4% effect = proportion_effectsize(target, baseline) n = NormalIndPower().solve_power( effect_size=effect, alpha=0.05, power=0.8, alternative='two-sided' ) print(round(n)) # roughly 39,000 users per group
74

Write Python code to test whether conversion differs between control and treatment.

Save to ▾
Show answer
from statsmodels.stats.proportion import proportions_ztest, proportion_confint conversions = [1180, 1295] # control, treatment visitors = [30000, 30100] z, p = proportions_ztest(conversions, visitors) print(f'z = {z:.2f}, p = {p:.4f}') for c, n in zip(conversions, visitors): low, high = proportion_confint(c, n, method='wilson') print(f'rate {c/n:.3%} 95% CI {low:.3%} to {high:.3%}')
75

Write SQL to calculate users, conversions and conversion rate for each test variant.

Save to ▾
Show answer
SELECT a.variant, COUNT(DISTINCT a.user_id) AS users, COUNT(DISTINCT o.user_id) AS converters, ROUND(100.0 * COUNT(DISTINCT o.user_id) / COUNT(DISTINCT a.user_id), 2) AS conversion_pct FROM experiment_assignments a LEFT JOIN orders o ON o.user_id = a.user_id AND o.order_ts >= a.assigned_ts WHERE a.experiment_id = 'checkout_v2' GROUP BY a.variant;
76

Write Python code for a simple difference-in-differences estimate of a store pilot.

Save to ▾
Show answer
import pandas as pd # df columns: store_type ('pilot' or 'control'), period ('pre' or 'post'), sales avg = df.pivot_table(index='store_type', columns='period', values='sales', aggfunc='mean') pilot_change = avg.loc['pilot', 'post'] - avg.loc['pilot', 'pre'] control_change = avg.loc['control', 'post'] - avg.loc['control', 'pre'] did = pilot_change - control_change print(f'Estimated pilot effect per store: {did:.1f}')
77

Write Python code to bootstrap a 95% confidence interval for the difference in revenue per user between two groups.

Save to ▾
Show answer
import numpy as np rng = np.random.default_rng(42) control = df.loc[df.variant == 'A', 'revenue'].to_numpy() treat = df.loc[df.variant == 'B', 'revenue'].to_numpy() diffs = [] for _ in range(5000): c = rng.choice(control, size=len(control), replace=True) t = rng.choice(treat, size=len(treat), replace=True) diffs.append(t.mean() - c.mean()) low, high = np.percentile(diffs, [2.5, 97.5]) print(f'95% CI for difference: {low:.2f} to {high:.2f}')
78

Write a Tableau calculated field that keeps only sales from the last 30 days.

TableauMedium
Save to ▾
Show answer
// Calculated field: Last 30 Days Flag DATEDIFF('day', [Order Date], TODAY()) <= 29 AND [Order Date] <= TODAY() // Put this field on Filters and keep True. // For a sales measure instead of a filter: // IF DATEDIFF('day', [Order Date], TODAY()) <= 29 THEN [Sales] END
79

Write pandas code to fill missing values in a column with the median of each group.

Save to ▾
Show answer
# Fill missing delivery_days with the median for that city df['delivery_days'] = ( df['delivery_days'] .fillna(df.groupby('city')['delivery_days'].transform('median')) ) # Rows in a city with no values at all stay NaN; fall back to overall median df['delivery_days'] = df['delivery_days'].fillna(df['delivery_days'].median())
80

Write SQL to calculate daily active users and the DAU/MAU stickiness ratio.

Save to ▾
Show answer
WITH dau AS ( SELECT CAST(event_ts AS DATE) AS day, COUNT(DISTINCT user_id) AS dau FROM events GROUP BY CAST(event_ts AS DATE) ) SELECT d.day, d.dau, (SELECT COUNT(DISTINCT e.user_id) FROM events e WHERE CAST(e.event_ts AS DATE) BETWEEN d.day - INTERVAL '29 days' AND d.day) AS mau, ROUND(100.0 * d.dau / (SELECT COUNT(DISTINCT e.user_id) FROM events e WHERE CAST(e.event_ts AS DATE) BETWEEN d.day - INTERVAL '29 days' AND d.day), 1) AS stickiness_pct FROM dau d ORDER BY d.day;
81

Write a UTM-tagged URL for a paid Instagram campaign promoting a summer sale, and give two naming rules.

Save to ▾
Show answer
https://www.example.com/sale?utm_source=instagram&utm_medium=paid_social&utm_campaign=summer_sale_2026&utm_content=reel_video_a -- Rule 1: always lower case with underscores; 'Instagram' and 'instagram' become separate rows -- Rule 2: keep a shared UTM sheet with allowed source and medium values so every team tags the same way
82

Write SQL to group raw page events into sessions using a 30-minute inactivity rule.

Save to ▾
Show answer
WITH ordered AS ( SELECT user_id, event_ts, LAG(event_ts) OVER (PARTITION BY user_id ORDER BY event_ts) AS prev_ts FROM page_events ), flagged AS ( SELECT *, CASE WHEN prev_ts IS NULL OR event_ts - prev_ts > INTERVAL '30 minutes' THEN 1 ELSE 0 END AS new_session FROM ordered ) SELECT user_id, event_ts, SUM(new_session) OVER (PARTITION BY user_id ORDER BY event_ts) AS session_no FROM flagged;
83

Write SQL to calculate sessions, orders and conversion rate by traffic source.

Save to ▾
Show answer
SELECT s.traffic_source, COUNT(*) AS sessions, COUNT(o.order_id) AS orders, ROUND(100.0 * COUNT(o.order_id) / COUNT(*), 2) AS conversion_pct FROM sessions s LEFT JOIN orders o ON o.session_id = s.session_id GROUP BY s.traffic_source ORDER BY sessions DESC; -- assumes at most one order per session; otherwise use COUNT(DISTINCT o.session_id)
84

Write SQL to find the first-touch and last-touch channel for every user who converted.

Save to ▾
Show answer
WITH touches AS ( SELECT t.user_id, t.channel, t.touch_ts, ROW_NUMBER() OVER (PARTITION BY t.user_id ORDER BY t.touch_ts) AS first_rank, ROW_NUMBER() OVER (PARTITION BY t.user_id ORDER BY t.touch_ts DESC) AS last_rank FROM marketing_touches t JOIN conversions c ON c.user_id = t.user_id AND t.touch_ts <= c.converted_ts ) SELECT user_id, MAX(CASE WHEN first_rank = 1 THEN channel END) AS first_touch, MAX(CASE WHEN last_rank = 1 THEN channel END) AS last_touch FROM touches GROUP BY user_id;
85

Write Python code to extract UTM source, medium and campaign from a column of landing page URLs.

Save to ▾
Show answer
import pandas as pd from urllib.parse import urlparse, parse_qs def get_utms(url): params = parse_qs(urlparse(str(url)).query) return pd.Series({ 'source': params.get('utm_source', ['(direct)'])[0].lower(), 'medium': params.get('utm_medium', ['(none)'])[0].lower(), 'campaign': params.get('utm_campaign', ['(not set)'])[0].lower(), }) df[['source', 'medium', 'campaign']] = df['landing_url'].apply(get_utms)
86

Write Excel formulas to calculate ROAS and CPA for each campaign and flag campaigns below a ROAS target.

Save to ▾
Show answer
-- B = Spend, C = Conversions, D = Revenue, target ROAS in H1 E2 (ROAS): =IFERROR(D2/B2, 0) F2 (CPA): =IF(C2=0, "No conversions", B2/C2) G2 (Flag): =IF(E2<$H$1, "Below target", "OK") -- Copy down, then add conditional formatting on G to highlight 'Below target'
87

Write SQL to calculate open rate and click-through rate for each email campaign.

Save to ▾
Show answer
SELECT s.campaign_id, COUNT(DISTINCT s.user_id) AS delivered, COUNT(DISTINCT o.user_id) AS opened, COUNT(DISTINCT c.user_id) AS clicked, ROUND(100.0 * COUNT(DISTINCT o.user_id) / COUNT(DISTINCT s.user_id), 1) AS open_rate, ROUND(100.0 * COUNT(DISTINCT c.user_id) / COUNT(DISTINCT s.user_id), 1) AS ctr FROM email_sends s LEFT JOIN email_opens o ON o.campaign_id = s.campaign_id AND o.user_id = s.user_id LEFT JOIN email_clicks c ON c.campaign_id = s.campaign_id AND c.user_id = s.user_id WHERE s.status = 'delivered' GROUP BY s.campaign_id;
88

Write pandas code to calculate customer acquisition cost and payback period in months for each channel.

Save to ▾
Show answer
# spend: channel, spend # customers: channel, customer_id, monthly_margin new_cust = customers.groupby('channel').agg( new_customers=('customer_id', 'nunique'), avg_margin=('monthly_margin', 'mean')) summary = spend.groupby('channel')['spend'].sum().to_frame().join(new_cust) summary['cac'] = summary['spend'] / summary['new_customers'] summary['payback_months'] = summary['cac'] / summary['avg_margin'] print(summary.round(1).sort_values('payback_months'))
89

Write SQL to find the number of days between each customer's consecutive orders and their average gap.

SQLMedium
Save to ▾
Show answer
WITH gaps AS ( SELECT customer_id, order_date, order_date - LAG(order_date) OVER ( PARTITION BY customer_id ORDER BY order_date) AS days_since_prev FROM orders ) SELECT customer_id, COUNT(*) AS orders, AVG(days_since_prev) AS avg_gap_days FROM gaps GROUP BY customer_id HAVING COUNT(*) > 1; -- order_date is DATE (PostgreSQL); use DATEDIFF in other databases
90

Write pandas code to convert daily sales into weekly totals, showing zero for days with no sales.

Save to ▾
Show answer
df['date'] = pd.to_datetime(df['date']) daily = (df.groupby('date')['sales'].sum() .asfreq('D', fill_value=0)) # adds missing days as 0 weekly = daily.resample('W-MON', label='left', closed='left').sum() print(weekly.head()) # W-MON with label/closed left gives weeks starting on Monday

Frequently asked questions

Why are practical questions important?

Most technical rounds include at least one live task. Practising the exact typing and reasoning makes you calm and fast in the real round.

How should I answer them?

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.