SQL Interview Mock sample questions with answers
10 questions from the SQL Interview Mock practice bank, spread across its domains. Pick your answer, then open the explanation to see why each option is right or wrong.
- Question 1Querying
Adding
GROUP BY statusto the previous query (where no row matches the WHERE clause) still returns one row with a count of 0.- A
True
- B
False
Show the answer and explanation
Answer: B
With
GROUP BY, the result has one row per group. Zero input rows form zero groups, so the query returns an empty result, not a row with 0. Dashboards that need a 0 for every status must start from a list of statuses and LEFT JOIN the counts onto it.Why the other options are wrong
A. With GROUP BY there is one row per group, and there are no groups.
- A
- Question 2Engine and Production
Which statements about PostgreSQL's REPEATABLE READ level are true? (Choose two.)
Choose 2.
- A
Rows inserted and committed by others after its snapshot never appear in its queries
- B
Two transactions can each read data and write disjoint rows, producing write skew
- C
It can read another transaction's uncommitted rows under heavy load
- D
A concurrent update to the same row is silently overwritten
- E
It behaves identically to SERIALIZABLE in PostgreSQL
Show the answer and explanation
Answer: A and B
PostgreSQL implements REPEATABLE READ as snapshot isolation: a stable snapshot (no non-repeatable reads, no phantoms) and first-updater-wins conflicts on the same row. It is stronger than the standard requires for phantoms, but it still allows write skew, which only SERIALIZABLE prevents.
Why the other options are wrong
C. Dirty reads are impossible at every PostgreSQL level.
D. PostgreSQL raises error 40001 instead of losing the other update.
E. SERIALIZABLE adds SSI checks that catch write skew; RR does not.
- A
- Question 3SQL Foundations
Which statements about identity columns and
serialin PostgreSQL are true? (Choose two.)Choose 2.
- A
Identity columns are SQL-standard syntax, while
serialis a PostgreSQL shorthand - B
With
GENERATED ALWAYS, an explicit id needsOVERRIDING SYSTEM VALUE - C
Identity columns guarantee gap-free numbers
- D
serialautomatically adds a UNIQUE constraint to the column - E
An identity value is given back to the sequence if the transaction rolls back
- F
GENERATED BY DEFAULTrejects explicit values just likeALWAYS
Show the answer and explanation
Answer: A and B
Prefer identity columns: they are standard, the sequence is tied tightly to the column, and ALWAYS prevents accidental manual values. Neither form guarantees consecutive numbers, and neither enforces uniqueness on its own; add PRIMARY KEY or UNIQUE.
Why the other options are wrong
C. Both use sequences, and sequence values are not returned on rollback, so gaps are normal.
D. serial only sets a default; you must add PRIMARY KEY or UNIQUE yourself.
E. nextval is never rolled back, which is how gaps appear.
F. BY DEFAULT accepts explicit values, like serial does.
- A
- Question 4Advanced SQL
In PostgreSQL, what does
SELECT ceil(-1.5), floor(-1.5);return?- A
-1and-2 - B
-2and-1(both rounded away from zero) - C
-1and-1 - D
-2and-2
Show the answer and explanation
Answer: A
ceilreturns the smallest integer not less than the argument, andfloorreturns the largest integer not greater than it. For negatives that meansceil(-1.5) = -1andfloor(-1.5) = -2.truncalways moves toward zero (trunc(-1.7) = -1).Why the other options are wrong
B. This swaps them; ceil of a negative number moves toward zero.
C. floor rounds down to the next lower integer, which is -2.
D. ceil rounds up, and -1 is greater than -1.5.
- A
- Question 5Querying
Which three statements about aggregates and NULL are true in PostgreSQL? (Choose three.)
Choose 3.
- A
COUNT(DISTINCT x)ignores NULL values - B
MAX(x)over a column with only NULLs returns NULL - C
SUM(x)returns NULL if any input value is NULL - D
GROUP BY puts all NULL values of the key in a single group
- E
COUNT(x)returns NULL when every x is NULL
Show the answer and explanation
Answer: A and B and D
NULL inputs are skipped by aggregates. When nothing non-NULL remains, aggregates return NULL except
COUNT, which returns 0. GROUP BY and DISTINCT treat NULLs as equal, so they form one group.SUMdoes not become NULL because of a single NULL input.Why the other options are wrong
C. NULL inputs are skipped; the sum of the rest is returned.
E. COUNT returns 0, never NULL.
- A
- Question 6Engine and Production
An
intcolumncustomer_idhas a B-tree index. An ORM sends the filter as anumericliteral:WHERE customer_id = 42.0. The plan is a Seq Scan withFilter: ((customer_id)::numeric = 42.0). Why?- A
Numeric literals are not allowed to use any index in PostgreSQL
- B
The column is cast to numeric, so the integer index no longer matches
- C
The planner rounds 42.0 to 42 and finds no matching rows
- D
Statistics on
customer_idare missing, so the planner gives up on the index
Show the answer and explanation
Answer: B
When the operand types differ, PostgreSQL resolves the operator by casting one side. Here it casts
customer_idto numeric (the wider type), wrapping the indexed column in an expression. Passing parameters with the column type, as in= 42or$1::int, keeps the predicate sargable. The same thing happens withcustomer_id::text = '42'. Verified on PostgreSQL 18.Why the other options are wrong
A. Numeric comparisons can use indexes on numeric columns; the issue is the type mismatch.
C. No rounding happens; the comparison is done in numeric and returns correct rows slowly.
D. This is a type-resolution problem, not a statistics problem.
- A
- Question 7SQL Foundations
What does this return in PostgreSQL?
SELECT 0.1::float8 + 0.2::float8 = 0.3 AS f, 0.1::numeric + 0.2::numeric = 0.3 AS n;- A
f = true, n = true - B
f = false, n = true - C
f = true, n = false - D
f = false, n = false
Show the answer and explanation
Answer: B
realanddouble precisionare IEEE 754 binary types, so values such as 0.1 are approximations.numeric/decimalstores exact decimal digits, which is why money and quantities that must add up exactly belong in numeric (or in integer cents), never in float.Why the other options are wrong
A. Binary floating point cannot represent 0.1 and 0.2 exactly, so their float sum misses 0.3.
C. numeric stores decimal digits exactly, so 0.1 + 0.2 is exactly 0.3.
D. Only the binary floating-point comparison is inexact here.
- A
- Question 8Advanced SQL
A query defines
WINDOW w AS (PARTITION BY dept)andWINDOW f AS (ORDER BY salary ROWS BETWEEN 1 PRECEDING AND CURRENT ROW). Which uses are valid in PostgreSQL? (Choose two.)Choose 2.
- A
ROW_NUMBER() OVER (w ORDER BY salary DESC) - B
SUM(salary) OVER f - C
SUM(salary) OVER (f) - D
ROW_NUMBER() OVER (w PARTITION BY name) - E
ROW_NUMBER() OVER (f ORDER BY name)
Show the answer and explanation
Answer: A and B
A named window can be referenced bare (OVER w) or copied and extended (OVER (w …)). When copying, you may add an ORDER BY or frame only if the base lacks them; you can never change its PARTITION BY. A base that already has a frame can only be used bare. PostgreSQL reports "cannot copy window \"f\" because it has a frame clause".
Why the other options are wrong
C. Copying a window that has a frame clause is an error.
D. A referenced window's PARTITION BY cannot be overridden.
E. f already has an ORDER BY, and it cannot be overridden.
- A
- Question 9Querying
In PostgreSQL, a data-modifying statement in
WITH, such asWITH d AS (DELETE FROM u RETURNING *) SELECT 1, still runs to completion even if the main query never readsd.- A
True
- B
False
Show the answer and explanation
Answer: A
The documentation guarantees that data-modifying statements in WITH are executed exactly once and always to completion, independently of how much of their output the main query reads. Running the example deletes every row of
uwhile the main query returns just1.Why the other options are wrong
B. Unlike SELECT CTEs, which may be skipped if unused, DELETE/INSERT/UPDATE in WITH always runs fully.
- A
- Question 10Engine and Production
With the session set to tenant
a, theapprole runs an INSERT. What happens?CREATE POLICY p ON docs USING (tenant = current_setting('app.tenant', true)); -- no WITH CHECK clause; app has INSERT on docs SET app.tenant = 'a'; INSERT INTO docs (id, tenant, body) VALUES (3, 'b', 'z');- A
It succeeds, because USING only filters which rows a query can read
- B
It fails with "new row violates row-level security policy"
- C
It succeeds, but the row is invisible to tenant
aafterwards - D
It is silently skipped, and the INSERT reports zero rows
Show the answer and explanation
Answer: B
A policy created without
FORapplies to all commands. When it has noWITH CHECK, PostgreSQL uses the USING expression to check rows being inserted or updated. A tenant therefore cannot write rows for another tenant. Verified in PostgreSQL 18: the INSERT, and an UPDATE moving a row to tenantb, both fail with "new row violates row-level security policy".Why the other options are wrong
A. For an ALL policy without WITH CHECK, the USING expression also checks new rows.
C. The row is rejected before it is written, so nothing is stored.
D. Inserts that fail a policy check raise an error rather than being skipped.
- A
Practise all 450 SQL questions
Start with the free 15-question diagnostic. It shows where to focus, and your results carry over if you sign up.