What the Capital One CodeSignal Data Analyst Assessment Actually Asks (Candidate Reports, 2021–2026)
Capital One does not publish the format of its data analyst screen. What exists is a trail of candidate reports on Blind from 2021 to 2025 and a handful of prep guides dated 2025 and 2026, and they mostly agree: about 70 minutes, around 14–15 questions, provided CSV or Excel datasets, mostly multiple choice, plus written SQL. This post lays out what those sources say and where they disagree, then works each named SQL pattern on a card-transactions schema — the shape the datasets are described as having — and ends with the two mistakes that cost points and a 2-week plan.
In this article
- The format, as reported (and where reports disagree)
- The schema used in every example below
- INNER vs LEFT JOIN — and when a join duplicates rows
- GROUP BY with COUNT, SUM, AVG, MIN, MAX
- CASE WHEN — conditional metrics and buckets
- CTEs and subqueries
- Window functions — ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, SUM OVER
- Date filtering — day, week, month, quarter, rolling window
- The two mistakes that cost points on dataset questions
- A 2-week prep plan
- FAQ
- Sources
The format, as reported (and where reports disagree)
Everything in this section is attributed. None of it comes from Capital One, and one reply on the most recent Blind thread — from someone posting as a Capital One employee in June 2025 — warns that the format "has changed in last 4 years". Read the dates as part of the claim.
| Source (dated) | What it says |
|---|---|
| Blind, Dec 2021 (Senior Data Analyst) | "70 minutes code signal assessment" |
| Blind, Feb 2022 | "14 questions instead of just 4" (vs the engineering test); "you can use a scripting language of choice (Python, R) or Excel"; "one question is a database question you have to use SQL for" |
| Blind, Nov 2022 | "14 questions and all of them were pretty lengthy"; "csv data to answer multiple choice questions"; "few sql questions" |
| Blind, Jun 2025 | "around 14–15 questions in 70 minutes"; "CSV files involved in some of the questions" |
| linkjob guide, Aug 2025 | "about 70 to 80 minutes"; "CSV data files"; joins, aggregations, GROUP BY, subqueries, CTEs, ROW_NUMBER / RANK |
| extrabrain guide, Feb 2026 | "roughly 70 to 80 minutes"; datasets that "resemble real customer, transaction, account, product, or campaign datasets"; the full SQL list worked below |
| finalroundai guide (analyst track) | "around 70 minutes"; "multiple MCQ questions and 4 to 6 general questions"; SQL joins, aggregations, window functions |
Where they agree: roughly 70 minutes, provided datasets (CSV or Excel), mostly multiple-choice questions answered from those datasets, and written SQL. Where they disagree:
- How many questions. Candidates say 14 to 15. The guides say "four to six" — which reads as the number of dataset sections, each carrying several multiple-choice items, rather than a different test. Plan for 14–15 individual answers.
- How much written SQL. One report says one database question; another says "few sql questions". Prepare for more than one.
- Which tool for the dataset questions. Candidates report a free choice — Excel, Python, R, or SQL. One February 2022 candidate found Excel "tedious" but quicker than SQL for the dataset items; a January 2023 reply on the same thread advises "Do not attempt the SQL until the very end. Work really quickly." Time is the constraint every report mentions.
What this means for prep: the multiple-choice questions are dataset questions, so the answer is a number, and a query that runs but double-counts is simply wrong. The SQL you need is not exotic — it is the six patterns below, executed without fan-out and at the right grain, under a clock.
The schema used in every example below
The February 2026 guide describes the datasets as resembling "customer, transaction, account, product, or campaign" tables. Capital One is a card issuer, so every example here runs on a card-transactions schema — the synthetic fraud ledger inside SQL Quest's Banking track, which you can query in the browser. Column names are real:
| Table | Columns | Grain |
|---|---|---|
| accounts | account_id, email, signup_at, country, device_fingerprint, ip_block, status | one row per account |
| merchants | merchant_id, name, category, country, risk_tier | one row per merchant |
| transactions | txn_id, account_id, amount, txn_at, merchant_id, lat, lng, status | one row per card transaction |
| chargebacks | chargeback_id, txn_id, account_id, merchant_id, reason_code, opened_at, resolved_at, status, related_chargeback_id | one row per dispute |
The last column is the one to internalise. Before any query, say the grain out loud: one row per what? Accounts have many transactions; transactions have zero or more chargebacks. Every mistake in the "two mistakes" section comes from forgetting one of those arrows.
INNER vs LEFT JOIN — and when a join duplicates rows
Both guides open with joins, and the February 2026 one adds the phrase that matters: "understanding when joins create duplicate rows". The classic dataset question is accounts that have never transacted. INNER JOIN cannot answer it — it drops exactly the rows you want.
SELECT a.account_id, a.country, a.signup_at
FROM accounts a
LEFT JOIN transactions t ON t.account_id = a.account_id
WHERE t.txn_id IS NULL
ORDER BY a.signup_at;
The mirror question — how many transactions per account, including zero — is where the duplicate-row trap lives. LEFT JOIN keeps every account, but each account now appears once per transaction. That is correct here, because you are about to count them; it is wrong the moment you SUM something from the accounts side.
SELECT a.account_id,
COUNT(t.txn_id) AS txn_count -- COUNT(*) would return 1 for a no-transaction account
FROM accounts a
LEFT JOIN transactions t ON t.account_id = a.account_id
GROUP BY a.account_id
ORDER BY txn_count DESC;
GROUP BY with COUNT, SUM, AVG, MIN, MAX
The guides list all five aggregates by name. A dataset question will ask for one number per category — which merchant category has the highest average transaction? — and the multiple-choice options will include the answers you get from the common mistakes (forgetting to filter refunds, averaging the wrong thing).
SELECT m.category,
COUNT(*) AS txn_count,
SUM(t.amount) AS total_spend,
ROUND(AVG(t.amount), 2) AS avg_amount,
MIN(t.amount) AS smallest,
MAX(t.amount) AS largest
FROM transactions t
JOIN merchants m ON m.merchant_id = t.merchant_id
WHERE t.status = 'completed' -- filter rows BEFORE grouping
GROUP BY m.category
HAVING COUNT(*) >= 20 -- filter GROUPS after
ORDER BY avg_amount DESC;
Two things to have cold: the WHERE / HAVING split (row filter before grouping, group filter after), and that this join is safe — merchants is one row per merchant, so joining it onto transactions cannot multiply anything. The unsafe direction is the next section but one.
▶ Write this one yourself: Transaction Share by Merchant Category
CASE WHEN — conditional metrics and buckets
The February 2026 guide names "CASE WHEN for conditional metrics and buckets". Both shapes are one query each. Buckets:
SELECT
CASE
WHEN amount < 25 THEN 'small'
WHEN amount < 250 THEN 'medium'
ELSE 'large'
END AS size_bucket,
COUNT(*) AS txn_count
FROM transactions
GROUP BY size_bucket
ORDER BY MIN(amount);
Conditional metrics — a rate computed inside one aggregate — is the pattern that turns up as "what share of high-risk merchants' transactions were declined?":
SELECT m.risk_tier,
COUNT(*) AS txn_count,
ROUND(100.0 * AVG(CASE WHEN t.status = 'declined' THEN 1 ELSE 0 END), 2) AS decline_rate_pct
FROM transactions t
JOIN merchants m ON m.merchant_id = t.merchant_id
GROUP BY m.risk_tier
ORDER BY decline_rate_pct DESC;
CTEs and subqueries
Both guides name CTEs; the February 2026 one adds "for breaking a problem into readable steps", which is the point. A typical two-step question: which accounts spend more than the average account? The average account's spend is itself an aggregate of an aggregate, so it needs two levels.
WITH per_account AS (
SELECT account_id, SUM(amount) AS total_spend
FROM transactions
WHERE status = 'completed'
GROUP BY account_id
),
benchmark AS (
SELECT AVG(total_spend) AS avg_spend FROM per_account
)
SELECT p.account_id, p.total_spend
FROM per_account p
CROSS JOIN benchmark b
WHERE p.total_spend > b.avg_spend
ORDER BY p.total_spend DESC;
The same query as a subquery is shorter and harder to read under a clock. When a question has two steps, write two CTEs and name them after the step. If the written SQL question is graded by a person, the names are free marks; if it is graded by output, they are free debugging.
Window functions — ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, SUM OVER
The February 2026 guide lists exactly six: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD and SUM OVER. They cover three question shapes.
Top-N per group (ROW_NUMBER / RANK / DENSE_RANK)
WITH merchant_spend AS (
SELECT m.country, m.name, SUM(t.amount) AS total_spend
FROM transactions t
JOIN merchants m ON m.merchant_id = t.merchant_id
WHERE t.status = 'completed'
GROUP BY m.country, m.name
),
ranked AS (
SELECT *,
RANK() OVER (PARTITION BY country ORDER BY total_spend DESC) AS rank_in_country
FROM merchant_spend
)
SELECT country, name, total_spend, rank_in_country
FROM ranked
WHERE rank_in_country <= 3
ORDER BY country, rank_in_country;
The three ranking functions differ only on ties: ROW_NUMBER breaks them arbitrarily (1, 2, 3), RANK leaves a gap (1, 1, 3), DENSE_RANK does not (1, 1, 2). A multiple-choice question that asks "how many merchants are in the top 3" is really asking which one you used.
Previous / next row (LAG / LEAD)
On a card ledger the LAG question is velocity: how many minutes since this account's previous transaction? Two charges seconds apart is what a fraud rule looks for, and it is the same shape as month-over-month change on any other table.
WITH ordered AS (
SELECT account_id, txn_id, amount, txn_at,
LAG(txn_at) OVER (PARTITION BY account_id ORDER BY txn_at) AS prev_txn_at
FROM transactions
)
SELECT account_id, txn_id, amount, txn_at,
ROUND((julianday(txn_at) - julianday(prev_txn_at)) * 1440, 1) AS minutes_since_prev
FROM ordered
WHERE prev_txn_at IS NOT NULL
ORDER BY minutes_since_prev
LIMIT 20;
Running totals (SUM OVER)
SELECT account_id, txn_at, amount,
SUM(amount) OVER (
PARTITION BY account_id
ORDER BY txn_at
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_spend
FROM transactions
WHERE status = 'completed'
ORDER BY account_id, txn_at;
Date filtering — day, week, month, quarter, rolling window
The February 2026 guide's last item is "date filtering by day, week, month, quarter, and rolling window". The syntax is dialect-specific — the examples below are SQLite, which is what runs in the browser on SQL Quest; the ideas move unchanged to PostgreSQL's date_trunc and INTERVAL.
-- by month
SELECT strftime('%Y-%m', txn_at) AS month,
COUNT(*) AS txn_count, SUM(amount) AS spend
FROM transactions
WHERE status = 'completed'
GROUP BY month
ORDER BY month;
-- rolling window: the 7 days ending on the ledger's last day
SELECT COUNT(*) AS txns_last_7_days
FROM transactions
WHERE txn_at >= datetime((SELECT MAX(txn_at) FROM transactions), '-7 days');
-- quarter: derive it from the month number
SELECT strftime('%Y', txn_at) || '-Q' || ((CAST(strftime('%m', txn_at) AS INTEGER) + 2) / 3) AS quarter,
SUM(amount) AS spend
FROM transactions
GROUP BY quarter;
The date trap on a dataset question: a filter like txn_at BETWEEN '2026-03-01' AND '2026-03-31' silently drops all of March 31 when txn_at carries a time — '2026-03-31 14:00' is greater than '2026-03-31'. Use >= '2026-03-01' AND < '2026-04-01'. On a multiple-choice question, the off-by-one-day answer is usually one of the options.
The two mistakes that cost points on dataset questions
On a written SQL question a reviewer may give partial credit for a query that is nearly right. On a multiple-choice dataset question there is no such thing: your number either matches an option or it does not, and the wrong options are the numbers you get from these two mistakes.
Mistake 1 — join fan-out
Joining a one-to-many table and then summing from the "one" side. Every transaction that has two chargebacks appears twice, and SUM(amount) is inflated by exactly the rows you duplicated.
SELECT t.account_id, SUM(t.amount) AS total_spend, COUNT(c.chargeback_id) AS disputes
FROM transactions t
LEFT JOIN chargebacks c ON c.txn_id = t.txn_id
GROUP BY t.account_id;
WITH disputes_per_txn AS (
SELECT txn_id, COUNT(*) AS disputes
FROM chargebacks
GROUP BY txn_id
)
SELECT t.account_id,
SUM(t.amount) AS total_spend,
SUM(COALESCE(d.disputes, 0)) AS disputes
FROM transactions t
LEFT JOIN disputes_per_txn d ON d.txn_id = t.txn_id
GROUP BY t.account_id;
The test for fan-out is one sentence: does this join multiply the thing I am summing? If the joined table can have more than one row per key on my side, yes. Pre-aggregate it, or count with COUNT(DISTINCT t.txn_id) and sum from a CTE that never saw the join.
Mistake 2 — the wrong grain
Answering "average transaction amount per country" by averaging each account's average. The numbers differ because an account with one transaction and an account with a thousand get equal weight. Neither query is "wrong SQL"; one of them answers a different question.
-- grain: transaction (what "average transaction amount" means)
SELECT a.country, ROUND(AVG(t.amount), 2) AS avg_txn_amount
FROM transactions t
JOIN accounts a ON a.account_id = t.account_id
GROUP BY a.country;
-- grain: account (what "average account's average transaction" means)
WITH per_account AS (
SELECT account_id, AVG(amount) AS avg_amount
FROM transactions
GROUP BY account_id
)
SELECT a.country, ROUND(AVG(p.avg_amount), 2) AS avg_of_account_avgs
FROM per_account p
JOIN accounts a ON a.account_id = p.account_id
GROUP BY a.country;
Read the question for its noun. "Per transaction", "per account", "per merchant" is the grain; the GROUP BY must produce one row per that noun and nothing else. If a question is ambiguous, the option list usually contains both numbers — which is itself the hint that grain is what is being tested.
A 2-week prep plan
Built for someone who already writes SELECT-FROM-WHERE and has an evening a day. Every item maps to a section above; the timed runs at the end are there because every candidate report mentions the clock.
Week 1 — the patterns, one per evening
- Day 1 — grain. Take the four-table schema above and, for ten plausible questions, write only the
GROUP BYline and the expected row count. No SELECT list. This is the skill the dataset questions test. - Day 2 — joins. INNER vs LEFT, the
IS NULLanti-join, andCOUNT(col)vsCOUNT(*)after a LEFT JOIN. Then deliberately fan out a join and watch the SUM change. Joins practice set. - Day 3 — aggregates. All five aggregates,
WHEREbefore,HAVINGafter. Write each query twice: once with the filter in the right place, once wrong, and see which answer a multiple-choice question would list. - Day 4 — CASE WHEN. Buckets and conditional rates. CASE WHEN practice set.
- Day 5 — CTEs. Rewrite every day-3 query that needed a subquery as named CTEs. Say each CTE's grain out loud.
- Day 6 — windows. Top-N per group with all three ranking functions on the same data; LAG for gaps; SUM OVER for running totals. Window functions practice set.
- Day 7 — dates. Month, quarter, rolling 7-day, and the end-of-range trap. In the tool you plan to use on the day, not just SQL.
Week 2 — speed, tools, and the clock
- Day 8 — pick your dataset tool. Candidates report a free choice of Excel, Python, R or SQL for the multiple-choice items. Load a CSV into each and time a GROUP BY and a two-table join. Keep the fastest one; it will not be the one you like most.
- Day 9 — fan-out drills. Ten questions on the card ledger where the wrong answer comes from a duplicated join. The Capital One practice cut is built around exactly this.
- Day 10 — grain drills. Ten questions where two grains give two numbers; write both, pick the one the wording asks for.
- Day 11 — window and date mix. Period-over-period change with LAG, then top-N within a date range.
- Day 12 — timed run 1. 70 minutes, 14 questions of your own, CSV in your chosen tool, one written SQL question at the end. Note where the time went.
- Day 13 — fix the slow spots. Whatever ate the time on day 12 — usually loading and cleaning the CSV, not the SQL.
- Day 14 — timed run 2, then stop. A second 70-minute run. The January 2023 Blind advice, from one candidate and worth exactly that, was to leave the written SQL for the end and move quickly through the dataset items; decide in this run whether that order suits you.
Practice the Capital One cut on real bank data
Joins at the right grain, GROUP BY, CTEs and window functions on FDIC bank data, plus the card-transactions ledger every example above ran on. Runs in the browser, no signup; the AI tutor explains why a wrong answer is wrong instead of just marking it.
Start with one question: Transaction Share by Merchant Category →FAQ
How long is the Capital One CodeSignal data analyst assessment and how many questions does it have?
Candidate reports on Blind describe a 70-minute assessment (a December 2021 report for a Senior Data Analyst role, and a June 2025 report of "around 14–15 questions in 70 minutes"). Two earlier reports (February and November 2022) say 14 questions. Prep guides dated August 2025 and February 2026 say roughly 70 to 80 minutes; those guides count "four to six" questions, apparently counting the dataset sections rather than the individual multiple-choice items. Capital One does not publish the format, and a Capital One employee replied on the June 2025 thread that the format had changed over the previous four years — so treat every number as candidate-reported and dated.
Is the Capital One data analyst CodeSignal test multiple choice or written SQL?
Mostly multiple choice over provided datasets, plus written SQL. A November 2022 Blind report describes "data analysis questions where you had to use the csv data to answer multiple choice questions" and "few sql questions"; a February 2022 reply says "one question is a database question you have to use SQL for" and that the rest can be answered with "a scripting language of choice (Python, R) or Excel". The reports differ on whether the written SQL is one question or a few, so prepare for more than one.
What SQL topics does the Capital One data analyst assessment cover?
The prep guides name: INNER JOIN and LEFT JOIN, including when a join creates duplicate rows; GROUP BY with COUNT, SUM, AVG, MIN and MAX; CASE WHEN for conditional metrics and buckets; CTEs and subqueries; window functions ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD and SUM OVER; and date filtering by day, week, month, quarter and rolling window (extrabrain, February 2026; linkjob, August 2025). Candidate reports add that the multiple-choice questions are dataset questions — the answer is a number — so join fan-out and aggregating at the wrong grain are the mistakes that cost points.
Can I use Excel or Python instead of SQL on the Capital One CodeSignal assessment?
For the dataset multiple-choice questions, candidate reports say yes: a February 2022 Blind reply says you can use "a scripting language of choice (Python, R) or Excel", and only the database question requires SQL. Guides dated 2025 and 2026 describe the same choice. Pick the tool you are fastest in for the dataset questions — several reports say time is the constraint — and keep SQL for the written question.
Where can I practice SQL for the Capital One data analyst assessment?
SQL Quest's Capital One page is a practice cut built from the same public descriptions: joins at the right grain, GROUP BY, CTEs and window functions on real FDIC bank data, plus a synthetic card-transactions ledger for the fan-out and velocity patterns. It runs in the browser with no signup, and free challenges include the Easy and Medium ones; Hard challenges are on Pro ($29/mo, $99/yr or $199 lifetime). The topic pages for joins, window functions and CASE WHEN cover the individual patterns.
Sources
Read on 7 September 2026. Nothing on this page comes from Capital One; every format claim is one of these, dated.
- Blind — "Has anyone attempted Code Signal assessment for data analyst position at Capital One?" (thread from September 2021; the 1 December 2021 reply reports a "70 minutes code signal assessment" for a Senior Data Analyst role).
- Blind — "Captial one Code signal for Data Analysis" (February 2022; "14 questions instead of just 4", tool choice, "one question is a database question you have to use SQL for"; the January 2023 reply on pacing).
- Blind — "Capital One Senior Data Analyst Codesignal Assessment" (thread from March 2022; the November 2022 reply: "14 questions", "csv data to answer multiple choice questions", "few sql questions").
- Blind — "Capital One CodeSignal for Data Analysts" (June 2025; "around 14–15 questions in 70 minutes", CSV files; the reply from a Capital One account that the format "has changed in last 4 years").
- linkjob.ai — "Preparation for Capital One Data Analyst CodeSignal Question", Peter Liu, 1 August 2025.
- extrabrain.app — "Capital One Data Analyst CodeSignal Questions: SQL, Python, and OA Prep", 11 February 2026.
- finalroundai.com — "Capital One CodeSignal Questions for Business Analyst" (undated; the analyst-track format and SQL topics).