Databases & SQL Interview Questions: Querying Practice

Reviewed by Mark Dickie · Last updated

SQL querying is the practice of writing declarative statements to retrieve and transform data stored in a relational database. For interviews, you need fluency with JOINs across multiple tables, GROUP BY with aggregation, subqueries and CTEs, and window functions such as ROW_NUMBER and RANK. You should also know how indexes affect performance and be able to read an EXPLAIN plan to spot full table scans.

What types of SQL queries come up in interviews?

ConceptWhat interviewers testCommon tasks
INNER / LEFT JOINMatching rows across tablesFind customers with no orders
GROUP BY + HAVINGAggregation with filteringCount orders per user, keep only > 5
Window functionsRanking and running totalsTop 3 products per category
Subqueries / CTEsBreaking logic into stepsCompare salary to department average
Set operationsCombining result setsUNION of two customer lists

How do you approach a SQL interview question?

  1. Clarify the schema and expected output before writing anything. Ask which columns the result should contain and whether duplicates matter.
  2. Identify the granularity of the output: one row per what?
  3. Pick the JOINs first, then add filtering and aggregation on top.
  4. Test with edge cases like NULL values or ties in ranking functions.
  5. If asked about performance, check the WHERE clause for non-sargable predicates and mention whether an index would help.

Key facts

  • Tarmac has 66 Databases & SQL interview questions on this topic, 10 of them on this page, at difficulty 1–3 of 5.
  • Tarmac tracked 8,648 job postings asking for Databases & SQL in August 2026.
  • Roles asking for Databases & SQL advertise a median base salary of US$166,400, across 1,383 job postings as of August 2026.
  • Tarmac last reviewed these Databases & SQL interview questions on 7 September 2026.

At a glance

Questions10 shown · 66 in the bank
Difficulty1–3 of 5
FormatsMultiple choice, True / false, Code output, Coding exercise, Fill in the blank, Find the bug, Short answer
Interactive1 run your code against tests, in the app

What you'll review

  1. select basics
  2. joins
  3. window functions
  4. aggregation

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

Databases & SQL/querying/select-basics

Which keyword removes duplicate rows from a SELECT result set?#

Options

Show answer

DISTINCT removes duplicate rows from a SELECT result set. Writing SELECT DISTINCT collapses rows that are identical across all selected columns into a single row. UNIQUE is a column or table constraint rather than a query keyword, and GROUP BY aggregates rows instead of simply de-duplicating them.

Why:

SELECT DISTINCT collapses rows that are identical across all selected columns into one. UNIQUE is a column/table constraint, not a query keyword, and GROUP BY aggregates rather than simply de-duplicating.

Databases & SQL/querying/joins

For an INNER JOIN, swapping which table is written first (A JOIN B vs B JOIN A) changes which rows appear in the result.#

Options

Show answer

False. INNER JOIN is commutative with respect to its result set, so A JOIN B and B JOIN A return the same rows (column ordering aside). Swapping which table is written first changes nothing about which rows appear. This is not true for outer joins, where LEFT JOIN and RIGHT JOIN keep different unmatched sides.

Why:

INNER JOIN is commutative with respect to its result set: A INNER JOIN B ON … and B INNER JOIN A ON … return the same rows (column ordering aside). This is not true for outer joins — LEFT JOIN and RIGHT JOIN keep different unmatched sides.

Databases & SQL/querying/window-functions

You can filter rows using a window function directly in the WHERE clause, e.g. WHERE ROW_NUMBER() OVER (ORDER BY id) <= 5.#

Options

Show answer

False. Window functions are evaluated after WHERE and GROUP BY but before ORDER BY, so they cannot appear in a WHERE clause — the engine hasn't computed them yet at that stage. To filter on a window function result, wrap the query in a subquery or CTE and apply WHERE in the outer query.

Why:

Window functions are evaluated after WHERE and GROUP BY but before ORDER BY, so they cannot appear in a WHERE clause — the engine hasn't computed them yet at that stage. To filter on a window function result, wrap the query in a subquery or CTE and apply WHERE in the outer query.

Databases & SQL/querying/select-basics

Given employees(name, salary) with rows (Ann, 90), (Bo, 70), (Cy, 90), (Di, 50), what does this query return?#

SELECT name, salary
FROM employees
WHERE salary >= 70
ORDER BY salary DESC, name ASC
LIMIT 2;

Options

Show answer
(Ann, 90), (Cy, 90)
Why:

WHERE salary >= 70 drops Di (50). The remaining three sort by salary DESC first, tying Ann and Cy at 90; the secondary name ASC orders Ann before Cy. LIMIT 2 then keeps the top two: (Ann, 90), (Cy, 90).

Databases & SQL/querying/aggregation

The users table has columns id (INTEGER) and email (TEXT). Write a query returning each email address that appears more than once, with columns email and cnt (how many times it appears), ordered by cnt descending, then email ascending.#

Starter code

-- Return: email, cnt (count > 1), ordered by cnt DESC, email ASC
SELECT

Your solution must pass

  • finds both duplicated emails with their counts

This one is written and run, not read. Solve it in the app and your code is executed against these tests and the hidden ones.

Databases & SQL/querying/joins

To keep every row from the left table even when there is no match, use customers c _____ JOIN orders o _____ o.customer_id = c.id.#

Show answer

To keep every row from the left table even when there is no match, use customers c **LEFT** JOIN orders o **ON** o.customer_id = c.id.

Why:

LEFT JOIN (synonym LEFT OUTER JOIN) preserves all left-hand rows, padding unmatched right-hand columns with NULL. The ON clause states the join predicate that decides which rows match — distinct from a later WHERE, which filters the joined result.

Databases & SQL/querying/joins

A LEFT JOIN between customers (left) and orders (right) returns a customer who has placed no orders. What appears in that row's orders columns?#

Options

Show answer

Every orders column holds NULL. A LEFT JOIN keeps each left-hand row even when no right-hand row matches the ON condition, filling the unmatched right-hand columns with NULL rather than omitting the row or substituting 0 or empty strings. This is exactly why a WHERE orders.id IS NULL filter after a LEFT JOIN finds customers who have placed no orders.

Why:

A LEFT JOIN keeps every left-hand row even when no right-hand row matches the ON condition; the unmatched right-hand columns are filled with NULL. This is exactly why a WHERE orders.id IS NULL filter after a LEFT JOIN finds customers with no orders.

Databases & SQL/querying/joins

customers(id, name) has (1, Ann), (2, Bo), (3, Cy). orders(customer_id, amount) has (1, 10), (1, 20), (2, 5). How many rows does this query return?#

SELECT c.name, o.amount
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id;

Options

Show answer
3 rows
Why:

An INNER JOIN emits one row per matching pair. Ann (id 1) matches two orders and Bo (id 2) matches one, giving 3 rows; Cy (id 3) has no orders and is excluded entirely. The result is (Ann, 10), (Ann, 20), (Bo, 5).

Databases & SQL/querying/joins

This is meant to be a LEFT JOIN listing every customer and their order count, but customers with no orders are dropped. Which line causes that?#

SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'shipped'
GROUP BY c.name;
Show answer

The bug is on line 4.

Why:

A WHERE predicate on the right-hand table runs after the join, and o.status = 'shipped' is never true for the NULL columns of an unmatched customer, so those rows are filtered out — silently turning the LEFT JOIN into an inner join. The fix is to move the condition into the join itself (LEFT JOIN orders o ON o.customer_id = c.id AND o.status = 'shipped') so unmatched customers survive.

Databases & SQL/querying/window-functions

Both GROUP BY and a window function like AVG(salary) OVER (...) can compute an average per group. What is the key difference in what each returns?#

Show answer

GROUP BY collapses each group down to a single output row, so you lose the individual rows and can only emit aggregates and the grouping columns. A window function computes the aggregate over a partition but keeps every input row, attaching the group-level value to each detail row alongside its own columns. So if you want each employee's row to also show their department average, you use a window function; GROUP BY would give you one row per department instead.

Why:

GROUP BY reduces N rows in a group to one summary row, discarding the detail. A window function (OVER (...)) evaluates the same aggregate across a partition but does not collapse rows — every original row survives and gets the computed value appended. That row-preserving behaviour is exactly why window functions are reached for when you need both the detail and a group-level metric (average, total, rank) in the same result set.

Sources

The official documentation these questions are checked against:

Related interview questions

Job market

See databases-sql salaries and hiring demand from live job postings.

The other 56 questions

This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 56 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.