Debug School

rakesh kumar
rakesh kumar

Posted on

SQL Concepts Every Developer Should Know: What, Why, When, Where, and How

A practical SQL interview guide with commands, examples, expected outputs, interview answers, and a colorful architecture diagram.

SQL helps applications store reliable data, retrieve related records, and calculate useful reports. Interviews test both syntax and reasoning: how NULL behaves, why a join multiplies rows, how ties affect ranking, and when an index helps.

This guide follows the learning structure of the reference JavaScript blog: a quick overview, What/Why/When/Where/How explanations, practical examples, pitfalls, and a colored concept map at the bottom. The SQL examples and explanations are original.

Dialect: PostgreSQL. Most SELECT examples also work in modern MySQL and SQLite. RETURNING, permission syntax, DDL transactions, date functions, and execution plans vary by database. Outputs below are expected results; numeric display formatting can vary by client. Tables without meaningful ordering explicitly use ORDER BY.

Practice rule: Run the setup once. Each numbered section starts from the original setup, independently of changes made in other sections. Use a practice database. Mutation examples that use BEGIN and ROLLBACK leave the original data intact.

Quick overview

Area Important concepts Typical interview task
Structure CREATE, ALTER, constraints, keys, normalization Design reliable tables
Read SELECT, DISTINCT, WHERE, ORDER BY, LIMIT Filter and paginate records
Calculate Aggregates, GROUP BY, HAVING, CASE, NULL Department reports
Combine Joins, subqueries, EXISTS, UNION, CTE Missing relationships and comparisons
Analyze ROW_NUMBER, RANK, DENSE_RANK, SUM OVER, LAG Top salaries and running totals
Change INSERT, UPDATE, DELETE, TRUNCATE, DROP Modify data safely
Reliability Transactions, savepoints, ACID, isolation Avoid partial updates
Performance/security Indexes, EXPLAIN, views, GRANT, REVOKE, parameters Efficient and authorized access

Shared sample database

CREATE TABLE departments (
  dept_id INTEGER PRIMARY KEY,
  dept_name VARCHAR(40) NOT NULL UNIQUE
);
CREATE TABLE employees (
  emp_id INTEGER PRIMARY KEY,
  emp_name VARCHAR(40) NOT NULL,
  dept_id INTEGER REFERENCES departments(dept_id),
  salary INTEGER NOT NULL CHECK (salary >= 0),
  manager_id INTEGER REFERENCES employees(emp_id),
  email VARCHAR(100)
);
INSERT INTO departments VALUES
(10, 'Engineering'), (20, 'Sales'), (30, 'HR'), (40, 'Finance');
INSERT INTO employees VALUES
(1, 'Asha',  10, 90000, NULL, 'asha@example.com'),
(2, 'Ravi',  10, 70000, 1,    'ravi@example.com'),
(3, 'Neha',  20, 70000, 1,    'neha@example.com'),
(4, 'Imran', 20, 50000, 3,    NULL),
(5, 'Meera', 30, 60000, 1,    'meera@example.com'),
(6, 'Kabir', NULL, 40000, 1,  NULL);
Enter fullscreen mode Exit fullscreen mode

Expected setup result: two tables created; four departments and six employees inserted.

emp_id emp_name dept_id salary manager_id email
1 Asha 10 90000 NULL asha@example.com
2 Ravi 10 70000 1 ravi@example.com
3 Neha 20 70000 1 neha@example.com
4 Imran 20 50000 3 NULL
5 Meera 30 60000 1 meera@example.com
6 Kabir NULL 40000 1 NULL

1. CREATE TABLE and constraints

What: CREATE defines a table; constraints enforce valid data.

Why: Rules remain enforced across all applications.

When and where: When designing tables and relationships.

How — example:

CREATE TABLE tasks (
  task_id INTEGER PRIMARY KEY,
  title VARCHAR(100) NOT NULL,
  status VARCHAR(12) DEFAULT 'open',
  owner_id INTEGER REFERENCES employees(emp_id),
  CHECK (status IN ('open', 'done'))
);
INSERT INTO tasks (task_id, title, owner_id)
VALUES (1, 'Review SQL', 2);
SELECT task_id, title, status, owner_id FROM tasks ORDER BY task_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

task_id title status owner_id
1 Review SQL open 2

Interview question: PRIMARY KEY vs UNIQUE vs FOREIGN KEY?

Answer: A primary key uniquely identifies rows and cannot be NULL. UNIQUE prevents duplicate non-NULL values; PostgreSQL allows multiple NULLs by default. A foreign key requires a matching referenced key for non-NULL values.

2. SELECT and aliases

What: SELECT chooses expressions and columns; AS gives an output label.

Why: Retrieve exactly the fields needed.

When and where: API responses and reports.

How — example:

SELECT emp_name AS name, salary * 12 AS annual_salary
FROM employees WHERE emp_id = 2;
Enter fullscreen mode Exit fullscreen mode

Expected output:

name annual_salary
Ravi 840000

Interview question: Does SELECT change stored data?

Answer: An ordinary SELECT reads data. Calculating annual_salary does not update salary.

3. DISTINCT

What: DISTINCT removes duplicate result rows.

Why: Return unique values.

When and where: Filter options and unique lists.

How — example:

SELECT DISTINCT salary FROM employees ORDER BY salary DESC;
Enter fullscreen mode Exit fullscreen mode

Expected output:

salary
90000
70000
60000
50000
40000

Interview question: DISTINCT vs GROUP BY?

Answer: DISTINCT deduplicates projected rows. GROUP BY forms groups for aggregation; it does not imply output sorting.

4. WHERE, AND, OR, and NOT

What: WHERE retains rows whose condition is TRUE.

Why: Apply business filters before aggregation.

When and where: Search screens and data selection.

How — example:

SELECT emp_name, salary FROM employees
WHERE (dept_id = 10 OR dept_id = 20) AND NOT salary < 70000
ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Asha 90000
Ravi 70000
Neha 70000

Interview question: Why use parentheses with AND and OR?

Answer: AND has higher precedence than OR. Parentheses make the intended logic explicit.

5. IN, BETWEEN, and LIKE

What: IN tests membership, BETWEEN checks an inclusive range, and LIKE matches patterns.

Why: Express common filters clearly.

When and where: Search by selected departments, ranges, or names.

How — example:

SELECT emp_name, salary FROM employees
WHERE dept_id IN (10, 20)
  AND salary BETWEEN 50000 AND 90000
  AND emp_name LIKE 'N%'
ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Neha 70000

Interview question: Is BETWEEN inclusive?

Answer: Yes. LIKE uses % for zero or more characters and _ for one character. Case sensitivity depends on the database and collation.

6. ORDER BY, LIMIT, and OFFSET

What: ORDER BY sorts; LIMIT caps rows; OFFSET skips rows.

Why: Build predictable top-N results and pages.

When and where: Leaderboards and pagination.

How — example:

SELECT emp_name, salary FROM employees
ORDER BY salary DESC, emp_id ASC LIMIT 2 OFFSET 1;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Ravi 70000
Neha 70000

Interview question: Why add emp_id after salary?

Answer: It breaks salary ties deterministically. Deep OFFSET pages can be expensive; keyset pagination is often better.

Watch out: SQL Server uses different pagination syntax. Without ORDER BY, row order is not guaranteed.

7. NULL, IS NULL, and COALESCE

What: NULL represents a missing or unknown value; COALESCE chooses the first non-NULL expression.

Why: Handle incomplete data explicitly.

When and where: Optional emails and unmatched relationships.

How — example:

SELECT emp_name, COALESCE(email, 'Not provided') AS contact
FROM employees WHERE email IS NULL ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name contact
Imran Not provided
Kabir Not provided

Interview question: Why does email = NULL not find missing emails?

Answer: Ordinary comparison with NULL produces UNKNOWN. WHERE keeps TRUE rows only; use IS NULL or IS NOT NULL.

8. COUNT, SUM, AVG, MIN, and MAX

What: Aggregates summarize a set of rows.

Why: Calculate counts and numeric statistics.

When and where: Dashboards and totals.

How — example:

SELECT COUNT(*) AS people, COUNT(email) AS known_emails,
       SUM(salary) AS total, CAST(AVG(salary) AS INTEGER) AS average,
       MIN(salary) AS lowest, MAX(salary) AS highest
FROM employees;
Enter fullscreen mode Exit fullscreen mode

Expected output:

people known_emails total average lowest highest
6 4 380000 63333 40000 90000

Interview question: COUNT(*) vs COUNT(column)?

Answer: COUNT(*) counts rows; COUNT(column) counts non-NULL values. SUM and AVG ignore NULL inputs. On no matching rows COUNT returns 0, while these other aggregates return NULL.

9. GROUP BY and HAVING

What: GROUP BY forms groups; HAVING filters grouped results.

Why: Report only groups meeting a threshold.

When and where: Department budgets and activity summaries.

How — example:

SELECT dept_id, COUNT(*) AS people, SUM(salary) AS payroll
FROM employees WHERE dept_id IS NOT NULL
GROUP BY dept_id HAVING COUNT(*) >= 2 ORDER BY dept_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

dept_id people payroll
10 2 160000
20 2 120000

Interview question: WHERE vs HAVING?

Answer: WHERE filters input rows; HAVING filters groups after aggregation. Here NULL departments are excluded before counting.

10. CASE expressions

What: CASE chooses a value from conditions.

Why: Put conditional labels and calculations into a query.

When and where: Salary bands and conditional aggregation.

How — example:

SELECT emp_name, CASE WHEN salary >= 80000 THEN 'High'
 WHEN salary >= 60000 THEN 'Medium' ELSE 'Standard' END AS band
FROM employees ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name band
Asha High
Ravi Medium
Neha Medium
Imran Standard
Meera Medium
Kabir Standard

Interview question: Can CASE calculate conditional counts?

Answer: Yes: SUM(CASE WHEN condition THEN 1 ELSE 0 END) counts matching rows. CASE is an expression, not a separate query.

11. INNER JOIN

What: An inner join returns matching row combinations.

Why: Combine facts from related tables.

When and where: Employee and department reports.

How — example:

SELECT e.emp_name, d.dept_name FROM employees e
INNER JOIN departments d ON d.dept_id = e.dept_id ORDER BY e.emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name dept_name
Asha Engineering
Ravi Engineering
Neha Sales
Imran Sales
Meera HR

Interview question: Why is Kabir absent?

Answer: His NULL department does not match a department row. Joins can multiply rows when a key matches multiple rows.

12. LEFT JOIN and missing relationships

What: A left join preserves all left-side rows and fills unmatched right columns with NULL.

Why: Find parents without children.

When and where: Departments without employees.

How — example:

SELECT d.dept_name FROM departments d
LEFT JOIN employees e ON e.dept_id = d.dept_id
WHERE e.emp_id IS NULL ORDER BY d.dept_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

dept_name
Finance

Interview question: Why test e.emp_id rather than e.email?

Answer: emp_id is non-NULL for real matches. A nullable email could incorrectly classify a matched employee as missing.

Watch out: Filtering a right-side column in WHERE can remove unmatched rows. Put match restrictions in ON when those rows should remain.

13. RIGHT JOIN and FULL OUTER JOIN

What: RIGHT preserves the right side; FULL preserves unmatched rows from both sides.

Why: Audit relationships in both directions.

When and where: Reconciliation and missing department assignments.

How — example:

SELECT COALESCE(e.emp_name, '(no employee)') AS employee,
       COALESCE(d.dept_name, '(no department)') AS department
FROM employees e FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
WHERE e.emp_id IS NULL OR d.dept_id IS NULL
ORDER BY employee;
Enter fullscreen mode Exit fullscreen mode

Expected output:

employee department
(no employee) Finance
Kabir (no department)

Interview question: Can a RIGHT JOIN be rewritten as a LEFT JOIN?

Answer: Yes, swap table order. FULL JOIN additionally keeps unmatched rows from both sides.

Watch out: MySQL does not directly support FULL OUTER JOIN. Use a carefully constructed UNION ALL alternative there.

14. CROSS JOIN

What: CROSS JOIN produces every pair of rows.

Why: Generate combinations deliberately.

When and where: Product options or scheduling grids.

How — example:

SELECT e.emp_name, d.dept_name FROM employees e
CROSS JOIN departments d WHERE e.emp_id = 2 AND d.dept_id IN (10, 20)
ORDER BY d.dept_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name dept_name
Ravi Engineering
Ravi Sales

Interview question: How many rows result from N cross-joined with M?

Answer: N × M rows before filtering. Accidental cross joins can produce very large intermediate results.

15. SELF JOIN

What: A self join uses different aliases for the same table.

Why: Represent relationships inside one entity type.

When and where: Employee/manager hierarchies.

How — example:

SELECT e.emp_name AS employee, m.emp_name AS manager
FROM employees e LEFT JOIN employees m ON m.emp_id = e.manager_id
WHERE e.emp_id IN (1, 4) ORDER BY e.emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

employee manager
Asha NULL
Imran Neha

Interview question: Why use LEFT JOIN for managers?

Answer: It preserves employees without a manager, including Asha. INNER JOIN would omit them.

16. Scalar subqueries

What: A scalar subquery supplies one value to an outer query.

Why: Compare rows against a calculated benchmark.

When and where: Above-average salary questions.

How — example:

SELECT emp_name, salary FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees) ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Asha 90000
Ravi 70000
Neha 70000

Interview question: What if a scalar subquery returns multiple rows?

Answer: PostgreSQL raises an error. A scalar subquery must produce at most one row and one column; no rows gives NULL.

17. Correlated subqueries

What: A correlated subquery refers to a row of the outer query.

Why: Compare each row with its own group.

When and where: Above-department-average salary.

How — example:

SELECT e.emp_name, e.salary FROM employees e
WHERE e.salary > (SELECT AVG(x.salary) FROM employees x
                  WHERE x.dept_id = e.dept_id) ORDER BY e.emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Asha 90000
Neha 70000

Interview question: Does correlation always mean one physical execution per row?

Answer: No. That describes the dependency, but optimizers may transform execution. Inspect a plan instead of assuming performance.

18. EXISTS and NOT EXISTS

What: EXISTS checks whether a subquery produces any row.

Why: Test relationships without returning duplicate parent rows.

When and where: Anti-joins and existence filters.

How — example:

SELECT d.dept_name FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.dept_id)
ORDER BY d.dept_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

dept_name
Finance

Interview question: Why prefer NOT EXISTS over NOT IN for nullable data?

Answer: A NULL returned by a NOT IN subquery can make comparisons UNKNOWN and unexpectedly filter rows. NOT EXISTS tests matching rows directly.

19. UNION and UNION ALL

What: UNION removes duplicate combined rows; UNION ALL retains them.

Why: Combine compatible result sets.

When and where: Merging sources and event streams.

How — example:

SELECT salary FROM employees WHERE dept_id = 10
UNION
SELECT salary FROM employees WHERE dept_id = 20
ORDER BY salary;
Enter fullscreen mode Exit fullscreen mode

Expected output:

salary
50000
70000
90000

Interview question: What changes with UNION ALL?

Answer: It returns 50000, 70000, 70000, 90000 for this example. It avoids duplicate elimination. Both branches must have equal column counts and compatible types.

20. INTERSECT and EXCEPT

What: INTERSECT returns shared rows; EXCEPT returns rows present only in the left result.

Why: Compare datasets directly.

When and where: Common salaries or reconciliation.

How — example:

SELECT salary FROM employees WHERE dept_id = 10
INTERSECT
SELECT salary FROM employees WHERE dept_id = 20
ORDER BY salary;
Enter fullscreen mode Exit fullscreen mode

Expected output:

salary
70000

Interview question: What would EXCEPT return here?

Answer: 90000: it occurs in Engineering but not Sales. Without ALL, these set operations eliminate duplicates.

21. WITH and common table expressions

What: A CTE names a statement-scoped query result.

Why: Break complex SQL into readable steps.

When and where: Reusable report stages and derived totals.

How — example:

WITH payroll AS (
 SELECT dept_id, SUM(salary) AS total FROM employees
 WHERE dept_id IS NOT NULL GROUP BY dept_id
)
SELECT dept_id, total FROM payroll WHERE total >= 120000 ORDER BY dept_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

dept_id total
10 160000
20 120000

Interview question: CTE vs temporary table?

Answer: A CTE is scoped to one statement; a temporary table persists for its defined temporary lifetime. A CTE does not automatically cache results or make a query faster.

22. ROW_NUMBER, RANK, and DENSE_RANK

What: Window ranking functions number rows or salary peer groups.

Why: Solve ranking questions while preserving individual rows.

When and where: Top earners, ties, and deduplication.

How — example:

SELECT emp_name, salary,
 ROW_NUMBER() OVER (ORDER BY salary DESC, emp_id) AS row_num,
 RANK() OVER (ORDER BY salary DESC) AS salary_rank,
 DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees ORDER BY salary DESC, emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary row_num salary_rank dense_rank
Asha 90000 1 1 1
Ravi 70000 2 2 2
Neha 70000 3 2 2
Meera 60000 4 4 3
Imran 50000 5 5 4
Kabir 40000 6 6 5

Interview question: RANK vs DENSE_RANK with ties?

Answer: Both give Ravi and Neha rank 2. The next RANK is 4; the next DENSE_RANK is 3. ROW_NUMBER always assigns a unique position.

Watch out: Do not add emp_id to the RANK salary ordering if equal salaries should tie.

23. Second-highest distinct salary

What: Ranking distinct salary levels handles ties explicitly.

Why: Avoid confusing the second row with the second salary.

When and where: One of the most common SQL interview tasks.

How — example:

WITH ranked AS (
 SELECT emp_name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS r
 FROM employees
)
SELECT emp_name, salary FROM ranked WHERE r = 2 ORDER BY emp_name;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name salary
Neha 70000
Ravi 70000

Interview question: What happens if there is only one distinct salary?

Answer: This query returns no rows. SELECT MAX(salary) FROM employees WHERE salary < (SELECT MAX(salary) FROM employees) instead returns one row containing NULL.

24. Top N per group

What: PARTITION BY restarts window calculations for each group.

Why: Rank within departments rather than across the whole table.

When and where: Highest paid employee in each department.

How — example:

WITH ranked AS (
 SELECT emp_id, emp_name, dept_id, salary,
 DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS r
 FROM employees WHERE dept_id IS NOT NULL
)
SELECT dept_id, emp_name, salary FROM ranked WHERE r <= 1
ORDER BY dept_id, emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

dept_id emp_name salary
10 Asha 90000
20 Neha 70000
30 Meera 60000

Interview question: How do you return exactly two employees per department?

Answer: Use ROW_NUMBER with salary DESC, emp_id and filter <= 2. DENSE_RANK <= 2 returns the top two salary levels, potentially more than two people.

25. Running totals and LAG

What: SUM OVER computes cumulative values; LAG reads a previous row.

Why: Analyze progression without collapsing rows.

When and where: Time series and comparisons.

How — example:

SELECT emp_id, salary,
 SUM(salary) OVER (ORDER BY emp_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total,
 LAG(salary) OVER (ORDER BY emp_id) AS previous_salary
FROM employees ORDER BY emp_id;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id salary running_total previous_salary
1 90000 90000 NULL
2 70000 160000 90000
3 70000 230000 70000
4 50000 280000 70000
5 60000 340000 50000
6 40000 380000 60000

Interview question: Why specify ROWS for a running total?

Answer: It explicitly accumulates row by row. Default frames with ORDER BY can include all peers with the same ordering value, which changes results when values tie.

26. Finding duplicate values

What: GROUP BY plus HAVING identifies repeated values.

Why: Detect data quality problems.

When and where: Duplicate emails, business keys, or repeated salaries.

How — example:

SELECT salary, COUNT(*) AS occurrences FROM employees
GROUP BY salary HAVING COUNT(*) > 1 ORDER BY salary;
Enter fullscreen mode Exit fullscreen mode

Expected output:

salary occurrences
70000 2

Interview question: Does duplicate salary mean duplicate employee?

Answer: No. Define the business key first. To remove duplicate business records, ROW_NUMBER can identify extra rows using a deterministic keeper rule.

27. INSERT and RETURNING

What: INSERT adds rows; PostgreSQL RETURNING reports inserted values.

Why: Create records and immediately retrieve stored identifiers.

When and where: New employees and application create operations.

How — example:

BEGIN;
INSERT INTO employees (emp_id, emp_name, dept_id, salary, manager_id)
VALUES (7, 'Tara', 20, 55000, 3)
RETURNING emp_id, emp_name, salary;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id emp_name salary
7 Tara 55000

Interview question: Why specify column names in INSERT?

Answer: It makes intent clear and avoids depending on physical column order.

28. UPDATE

What: UPDATE modifies matching rows.

Why: Apply changes to existing records.

When and where: Salary changes and status updates.

How — example:

BEGIN;
UPDATE employees SET salary = salary + 5000 WHERE emp_id = 4
RETURNING emp_id, emp_name, salary;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id emp_name salary
4 Imran 55000

Interview question: What if WHERE is omitted?

Answer: Every row is updated. Confirm the intended target set and inspect the affected-row count.

29. DELETE

What: DELETE removes matching rows while retaining the table.

Why: Remove selected records.

When and where: Cleanup and record removal.

How — example:

BEGIN;
DELETE FROM employees WHERE emp_id = 6 RETURNING emp_id, emp_name;
SELECT COUNT(*) AS remaining FROM employees;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id emp_name
6 Kabir

Then:

remaining
5

Interview question: DELETE vs TRUNCATE vs DROP?

Answer: DELETE can filter rows; TRUNCATE empties tables without WHERE; DROP removes the table object. Foreign keys and transaction behavior matter.

30. ALTER TABLE

What: ALTER changes an existing table definition.

Why: Evolve a schema as requirements change.

When and where: Adding fields or constraints.

How — example:

BEGIN;
ALTER TABLE employees ADD COLUMN active BOOLEAN NOT NULL DEFAULT TRUE;
SELECT emp_name, active FROM employees WHERE emp_id = 1;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name active
Asha true

Interview question: Can adding a column lock a table?

Answer: Yes. Locking, rewrites, and cost depend on the engine and operation. Plan production migrations accordingly.

31. TRUNCATE and DROP TABLE

What: TRUNCATE removes all rows; DROP removes the table definition and its data.

Why: Reset staging data or remove unused objects.

When and where: Practice tables and controlled schema changes.

How — example:

BEGIN;
CREATE TABLE scratch_ids (id INTEGER);
INSERT INTO scratch_ids VALUES (1), (2);
TRUNCATE TABLE scratch_ids;
SELECT COUNT(*) AS remaining FROM scratch_ids;
DROP TABLE scratch_ids;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

remaining
0

DROP TABLE succeeds; ROLLBACK leaves no scratch_ids table because its creation was also in the transaction.

Interview question: Can TRUNCATE be rolled back?

Answer: In PostgreSQL, yes inside a transaction. Do not generalize to all engines: MySQL TRUNCATE causes an implicit commit. PostgreSQL takes an ACCESS EXCLUSIVE lock; identity reset requires RESTART IDENTITY.

32. CREATE VIEW

What: A view stores a query definition and exposes it like a table.

Why: Provide a reusable data interface.

When and where: Reports and restricted projections.

How — example:

BEGIN;
CREATE VIEW engineering_people AS
 SELECT emp_id, emp_name FROM employees WHERE dept_id = 10;
SELECT * FROM engineering_people ORDER BY emp_id;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id emp_name
1 Asha
2 Ravi

Interview question: View vs materialized view?

Answer: An ordinary view stores a query, not a separate result snapshot. A materialized view stores results that need refreshing. A view is not automatically a complete security boundary.

33. CREATE INDEX and EXPLAIN

What: An index is an access structure; EXPLAIN shows the planned execution strategy.

Why: Reduce work for suitable filters and joins.

When and where: Large tables and slow query investigation.

How — example:

BEGIN;
CREATE INDEX idx_employees_dept_salary ON employees (dept_id, salary);
EXPLAIN SELECT emp_id FROM employees WHERE dept_id = 10 AND salary >= 70000;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

CREATE INDEX succeeds. EXPLAIN returns a plan table; the exact plan and estimated costs depend on database statistics and configuration. On this six-row table, a sequential scan is plausible.

Interview question: Why might the optimizer ignore an index?

Answer: Small tables, low selectivity, statistics, and cost estimates can favor a sequential scan. Indexes consume space and add write overhead.

Watch out: A multicolumn B-tree is generally most effective when predicates constrain leading columns. EXPLAIN ANALYZE executes the statement; use care with mutations.

34. BEGIN, SAVEPOINT, ROLLBACK TO, COMMIT, and ROLLBACK

What: Transactions group work; savepoints allow partial undo.

Why: Avoid partial business operations.

When and where: Transfers, bookings, and related updates.

How — example:

BEGIN;
UPDATE employees SET salary = salary + 1000 WHERE emp_id = 2;
SAVEPOINT after_raise;
UPDATE employees SET salary = 0 WHERE emp_id = 2;
ROLLBACK TO SAVEPOINT after_raise;
SELECT emp_id, salary FROM employees WHERE emp_id = 2;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id salary
2 71000

Interview question: How would COMMIT change this example?

Answer: Replacing the final ROLLBACK with COMMIT would persist Ravi’s 71000 salary. ROLLBACK TO preserves earlier work in the active transaction; a full ROLLBACK undoes the transaction.

35. ACID and isolation

What: ACID means atomicity, consistency, isolation, and durability.

Why: Reason about failures and concurrent operations.

When and where: Booking inventory and financial transactions.

How — example:

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SHOW transaction_isolation;
SELECT salary FROM employees WHERE emp_id = 2;
ROLLBACK;
Enter fullscreen mode Exit fullscreen mode

Expected output:

transaction_isolation
repeatable read

Then:

salary
70000

Interview question: READ COMMITTED vs REPEATABLE READ in PostgreSQL?

Answer: READ COMMITTED uses a new snapshot for each statement; REPEATABLE READ uses a stable transaction snapshot. PostgreSQL also prevents phantom reads at REPEATABLE READ, but serialization anomalies remain possible. SERIALIZABLE can abort conflicting transactions, so applications must retry.

36. GRANT and REVOKE

What: GRANT gives privileges; REVOKE removes a specific grant.

Why: Apply least-privilege access.

When and where: Read-only reporting roles.

How — example:

-- Prerequisite: reporting_user already exists; run as an authorized owner/admin.
GRANT SELECT ON employees TO reporting_user;
REVOKE SELECT ON employees FROM reporting_user;
Enter fullscreen mode Exit fullscreen mode

Expected output:

With the prerequisites satisfied, PostgreSQL reports GRANT and REVOKE command completion. These statements do not return data rows.

Interview question: Does REVOKE guarantee the user can no longer read the table?

Answer: No. Access may still come through role membership, PUBLIC, ownership, or other privileges. Schema USAGE may also be required for access.

37. Normalization and relationships

What: Normalization separates facts to reduce redundant storage and update anomalies.

Why: Keep one authoritative place for each fact.

When and where: Relational schema design.

How — example:

SELECT e.emp_name, d.dept_name FROM employees e
JOIN departments d ON d.dept_id = e.dept_id WHERE e.emp_id = 2;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_name dept_name
Ravi Engineering

Interview question: Explain 1NF, 2NF, and 3NF.

Answer: 1NF uses atomic values and avoids repeating groups. 2NF removes partial dependencies on a composite candidate key. 3NF removes transitive dependencies of non-key attributes on keys. Here dept_name belongs in departments rather than being repeated for each employee.

38. Prepared statements and SQL injection

What: Prepared statements separate SQL structure from supplied values.

Why: Treat user input as values rather than executable SQL.

When and where: Login, search, and lookup endpoints.

How — example:

PREPARE employee_lookup(INTEGER) AS
 SELECT emp_id, emp_name FROM employees WHERE emp_id = $1;
EXECUTE employee_lookup(2);
DEALLOCATE employee_lookup;
Enter fullscreen mode Exit fullscreen mode

Expected output:

emp_id emp_name
2 Ravi

Interview question: Can parameters safely replace table names?

Answer: Normally parameters represent values, not SQL identifiers. Choose dynamic table or column names from an allowlist and quote them using trusted database APIs. Never concatenate untrusted values into SQL.

39. COMMIT: persist a successful transaction

What: COMMIT ends a transaction and persists its successful changes.

Why: Other transactions can then observe the committed result according to their isolation rules.

When and where: After all steps of a business operation succeed.

How — example:

CREATE TABLE commit_demo (id INTEGER PRIMARY KEY, message VARCHAR(40));
BEGIN;
INSERT INTO commit_demo VALUES (1, 'Saved successfully');
COMMIT;
SELECT id, message FROM commit_demo ORDER BY id;
DROP TABLE commit_demo;
Enter fullscreen mode Exit fullscreen mode

Expected output:

id message
1 Saved successfully

The row remains after COMMIT; the final DROP cleans up the demonstration table.

Interview question: Can a later ROLLBACK undo an already committed transaction?

Answer: No. A compensating operation, recovery procedure, or other database-specific mechanism is needed to reverse committed effects.

How a SELECT is logically evaluated

A useful interview model is: FROM/JOIN and ON → WHERE → GROUP BY and aggregates → HAVING → window calculations → SELECT projection → DISTINCT → ORDER BY → LIMIT/OFFSET. This is a reasoning model, not a promise about physical execution; the optimizer may transform operations while preserving semantics.

That model explains why you generally cannot use a SELECT alias in WHERE, why WHERE cannot directly filter a window function, and why a CTE or subquery helps filter a calculated rank.

Interview revision: common mistakes

Mistake Better answer or practice
Using = NULL Use IS NULL; understand UNKNOWN
Using NOT IN with nullable subquery results Consider correlated NOT EXISTS
Treating the second row as the second-highest salary Rank distinct salary levels
Assuming JOIN removes duplicates Check relationship cardinality
Filtering a LEFT JOIN right side carelessly Distinguish ON match conditions from WHERE filters
Ranking ties with a unique ID in every window ORDER BY Use unique IDs for ROW_NUMBER; preserve peers for salary ranking
Assuming GROUP BY sorts results Add ORDER BY
Assuming indexes always speed up queries Inspect the plan and workload
Claiming every CTE is materialized Optimization depends on engine and query
Claiming TRUNCATE cannot be rolled back anywhere State the engine; PostgreSQL supports transaction rollback
Assuming a view stores a refreshed data copy Distinguish ordinary and materialized views
Concatenating values into SQL Bind parameters

References and publishing notes

The following diagrams use Mermaid with explicit color styles. Paste them into a Markdown publisher with Mermaid support. If your publisher shows the code rather than rendering it, export the diagram as an image using a Mermaid renderer and upload that image to the post.

Colorful SQL architecture and interview concept map

This is a learning map of SQL responsibilities, followed by a query-processing architecture. The command groups are common teaching categories; their labels are not a universal SQL standard taxonomy.

flowchart TD
    ROOT["SQL: developer and interview essentials"]:::root
    ROOT --> STRUCT["Structure and integrity"]:::purple
    ROOT --> QUERY["Read and analyze"]:::blue
    ROOT --> WRITE["Changes and reliability"]:::orange
    ROOT --> OPERATE["Performance and access"]:::pink
    STRUCT --> DDL["CREATE • ALTER • DROP"]:::purple
    DDL --> RULES["Keys • constraints • normalization"]:::purple
    RULES --> SCHEMA["Valid rows and reliable relationships"]:::purple
    QUERY --> BASIC["SELECT • WHERE • DISTINCT • ORDER BY • LIMIT"]:::blue
    BASIC --> COMBINE["JOIN • UNION • INTERSECT • EXCEPT"]:::cyan
    COMBINE --> REPORT["GROUP BY • HAVING • CASE • NULL"]:::green
    REPORT --> ADV["Subqueries • EXISTS • WITH • window functions"]:::green
    ADV --> INTERVIEW["Second salary • top N • duplicates • running totals"]:::green
    WRITE --> DML["INSERT • UPDATE • DELETE • TRUNCATE"]:::orange
    DML --> TX["BEGIN • SAVEPOINT • COMMIT • ROLLBACK"]:::yellow
    TX --> SAFE["ACID • isolation • concurrency"]:::yellow
    OPERATE --> PERF["CREATE INDEX • EXPLAIN • CREATE VIEW"]:::pink
    PERF --> ACCESS["GRANT • REVOKE • prepared statements"]:::pink
    ACCESS --> GOAL["Efficient queries and controlled access"]:::pink
    classDef root fill:#0f172a,stroke:#38bdf8,color:#ffffff,stroke-width:3px;
    classDef purple fill:#f3e8ff,stroke:#9333ea,color:#581c87,stroke-width:2px;
    classDef blue fill:#dbeafe,stroke:#2563eb,color:#172554,stroke-width:2px;
    classDef cyan fill:#cffafe,stroke:#0891b2,color:#164e63,stroke-width:2px;
    classDef green fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:2px;
    classDef orange fill:#ffedd5,stroke:#ea580c,color:#7c2d12,stroke-width:2px;
    classDef yellow fill:#fef9c3,stroke:#ca8a04,color:#713f12,stroke-width:2px;
    classDef pink fill:#fce7f3,stroke:#db2777,color:#831843,stroke-width:2px;

How an application query reaches stored data

flowchart TD
    APP["Application: SQL and bound values"]:::blue --> CHECK["Parse, resolve names, check privileges"]:::purple
    CHECK --> OPT["Optimizer: choose an execution plan"]:::yellow
    STATS["Statistics and available indexes"]:::pink --> OPT
    OPT --> EXEC["Executor: scans, joins, filters, aggregates"]:::green
    EXEC --> CACHE["Buffer manager and cached pages"]:::cyan
    CACHE --> TABLES["Table and index storage"]:::orange
    TABLES --> CACHE
    CACHE --> EXEC
    EXEC --> RESULT["Result rows or affected-row count"]:::blue
    EXEC -.-> TX["Transaction and concurrency control"]:::purple
    TX -.-> WAL["Write-ahead log for durable changes"]:::pink
    WAL -.-> TABLES
    classDef blue fill:#dbeafe,stroke:#2563eb,color:#172554,stroke-width:2px;
    classDef purple fill:#f3e8ff,stroke:#9333ea,color:#581c87,stroke-width:2px;
    classDef yellow fill:#fef9c3,stroke:#ca8a04,color:#713f12,stroke-width:2px;
    classDef green fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:2px;
    classDef cyan fill:#cffafe,stroke:#0891b2,color:#164e63,stroke-width:2px;
    classDef orange fill:#ffedd5,stroke:#ea580c,color:#7c2d12,stroke-width:2px;
    classDef pink fill:#fce7f3,stroke:#db2777,color:#831843,stroke-width:2px;

Solid arrows show the simplified request/data path; dotted arrows show supporting write and transaction responsibilities. PostgreSQL has additional processes and mechanisms beyond this teaching diagram.

Top comments (0)