PostgreSQL Interview Guide
PostgreSQL Interview Guide
PostgreSQL
Interview Preparation Guide
From Zero to Confident — In One Guide
■ TABLE OF CONTENTS
Section 1 — Basic Questions (Q1–Q16) — Beginner Friendly
Section 2 — Intermediate Questions (Q1–Q16) — Core Interview Concepts
Section 3 — Advanced Questions (Q1–Q10) — Senior-Level Topics
Common Mistakes Section — Top 10 Interview Pitfalls
Rapid Revision Section — Last-Day Study Guide
· One-Liner Definitions (20 terms)
· Key Differences Table
· Must-Remember Rules (10 rules)
· Interview Mindset Tips (5 tips)
Symbol Meaning
■ Memory Tip One punchy line to lock the concept in your brain
■ Answer:
PostgreSQL is a free, open-source tool that stores your data in tables — like Excel, but much more
powerful. It follows the rules of SQL (Structured Query Language) and handles millions of rows without
breaking a sweat.
■ Example:
Think of it as a super-organized digital filing cabinet. Each drawer is a table. Each folder inside is a row.
Each paper in that folder is a column value.
■ Memory Tip: Postgres = Excel on steroids — powerful, free, and it doesn't crash!
■ Answer:
PostgreSQL supports ACID transactions (safe writes), JSON storage, full-text search, custom data types,
and it runs on Windows, Mac, and Linux. It's also highly extensible — you can add your own functions.
■ Example:
Key features: - ACID compliance (data stays safe) - JSONB support (store JSON blazing fast) - Full-text
search (search inside text) - Custom types & extensions - Runs everywhere
■ Answer:
A database is a container that holds all your data. A table is like a spreadsheet inside that container — it
has rows (records) and columns (fields). One database can hold hundreds of tables.
■ Example:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
grade INTEGER
);
■ Memory Tip: Database = warehouse. Table = shelf inside it. Row = box on the shelf.
■ Answer:
PostgreSQL has types for text (TEXT, VARCHAR), numbers (INTEGER, BIGINT, NUMERIC), dates
(DATE, TIMESTAMP), booleans (BOOLEAN), and even JSON (JSON, JSONB). JSONB stores JSON in
binary — much faster to query.
■ Example:
-- Common data types in action:
name TEXT,
age INTEGER,
price NUMERIC(10,2),
created TIMESTAMP,
is_active BOOLEAN,
meta JSONB
■ Memory Tip: TEXT for words, INTEGER for whole numbers, NUMERIC for money, JSONB for
flexible data.
■ Answer:
SERIAL is a shortcut that auto-generates an increasing number for a column. Every time you insert a
row, PostgreSQL picks the next number automatically. Perfect for ID columns.
■ Example:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
product TEXT
);
-- First row gets order_id = 1
-- Second row gets order_id = 2 (auto!)
■ Answer:
A primary key is a column (or group of columns) that uniquely identifies each row. No two rows can have
the same primary key. It also cannot be NULL (empty).
■ Example:
CREATE TABLE employees (
emp_id SERIAL PRIMARY KEY, -- unique, not null
emp_name TEXT NOT NULL
);
-- emp_id 1, 2, 3... never repeated
■ Memory Tip: Primary key = your passport number. Unique to you, never blank.
■ Answer:
A foreign key links one table to another. It stores the primary key of a related row in another table. This
enforces that you can't reference something that doesn't exist.
■ Example:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id)
);
-- customer_id must exist in customers table
■ Memory Tip: Foreign key = a hotel room key — it only works for a room that actually exists.
■ Answer:
Constraints are rules you set on a column to control what data is allowed. Common ones: NOT NULL
(can't be empty), UNIQUE (no duplicates), CHECK (must pass a condition), DEFAULT (use this value if
nothing is given).
■ Example:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
price NUMERIC CHECK (price > 0),
stock INTEGER DEFAULT 0,
sku TEXT UNIQUE
);
■ Memory Tip: Constraints = bouncers at a club — they decide what data gets in.
■ Answer:
WHERE filters rows BEFORE grouping. HAVING filters groups AFTER grouping. You use HAVING only
with GROUP BY. Think: WHERE is for individual rows, HAVING is for summarized groups.
■ Example:
-- WHERE: filter individual rows
SELECT * FROM orders WHERE amount > 100;
■ Memory Tip: WHERE = filter before cooking. HAVING = taste after cooking.
■ Answer:
ORDER BY sorts your results. Use ASC for smallest-to-largest (default) or DESC for largest-to-smallest.
You can sort by multiple columns.
■ Example:
SELECT name, salary
FROM employees
ORDER BY salary DESC, name ASC;
-- Highest salary first
-- Same salary? Sort by name A-Z
■ Answer:
DISTINCT removes duplicate rows from your result. If 10 rows have the same city, DISTINCT returns it
only once. It applies to the full row — or specific columns you list.
■ Example:
-- Without DISTINCT (may show duplicates)
SELECT city FROM customers;
■ Memory Tip: DISTINCT = a highlighter that marks each unique answer only once.
■ Answer:
DELETE removes specific rows (you pick which). TRUNCATE removes ALL rows instantly — much
faster. DROP removes the entire table (structure + data). DELETE can be rolled back; TRUNCATE
usually cannot.
■ Example:
DELETE FROM users WHERE id = 5; -- one row gone
TRUNCATE TABLE logs; -- all rows gone, fast
DROP TABLE old_reports; -- whole table gone
■ Memory Tip: Delete = erase a word. Truncate = rip out the page. Drop = burn the book.
■ Answer:
LIMIT controls how many rows to return. OFFSET skips a number of rows before returning. Together
they implement pagination — showing data in pages.
■ Example:
-- Page 1: first 10 rows
SELECT * FROM products LIMIT 10 OFFSET 0;
■ Memory Tip: LIMIT + OFFSET = a book — LIMIT is the page size, OFFSET is which page you're
on.
■ Answer:
A schema is a namespace — a logical folder — inside a database. It groups tables, views, and functions
together. The default schema is called 'public'. You can have multiple schemas for different teams or
modules.
■ Example:
CREATE SCHEMA sales;
CREATE TABLE [Link] (id SERIAL, ...);
■ Memory Tip: Schema = a department in a company. Same office (database), different team
rooms.
■ Answer:
CHAR(n) pads short strings with spaces to fill n characters — wastes space. VARCHAR(n) stores up to n
characters, no padding. TEXT has no length limit at all. In PostgreSQL, TEXT and VARCHAR perform
almost identically — use TEXT for simplicity.
■ Example:
-- CHAR(5): 'Hi' stored as 'Hi ' (padded)
-- VARCHAR(100): 'Hi' stored as 'Hi' (no waste)
-- TEXT: 'Hi' stored as 'Hi', no limit
■ Memory Tip: TEXT = stretchy jeans. VARCHAR = jeans with a size limit. CHAR = stiff jeans
padded with socks.
■ Answer:
Aggregate functions compute one value from many rows. Common ones: COUNT (how many rows),
SUM (total), AVG (average), MIN (smallest), MAX (largest). Always used with GROUP BY when breaking
down by category.
■ Example:
SELECT
department,
COUNT(*) AS total_staff,
AVG(salary) AS avg_salary,
MAX(salary) AS top_salary
FROM employees
GROUP BY department;
■ Memory Tip: Aggregates = measuring cups — they collapse many values into one
measurement.
■ Answer:
INNER JOIN returns only rows that match in BOTH tables. LEFT JOIN returns all rows from the left table
+ matching rows from the right (NULLs where no match). RIGHT JOIN is the opposite. FULL OUTER
JOIN returns everything from both tables.
■ Example:
-- INNER JOIN: only matched rows
SELECT [Link], [Link]
FROM orders o
INNER JOIN customers c ON o.customer_id = [Link];
■ Memory Tip: INNER = Venn diagram overlap only. LEFT = left circle fully + overlap.
■ Answer:
A self join joins a table to itself. Useful when rows in a table relate to other rows in the same table — like
an employee table where each employee has a manager who is also an employee.
■ Example:
SELECT
[Link] AS employee,
[Link] AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = [Link];
-- Same table, two different aliases
■ Memory Tip: Self join = looking in a mirror — same table, two different perspectives.
■ Answer:
A view is a saved query that looks like a table. You don't store data — it runs the query fresh every time.
Views simplify complex queries, restrict what columns users can see, and keep your SQL DRY (Don't
Repeat Yourself).
■ Example:
CREATE VIEW active_users AS
SELECT id, name, email
FROM users
WHERE is_active = TRUE;
■ Memory Tip: View = a window into your data — same data, just a specific angle.
■ Answer:
A materialized view stores the query result physically on disk. Unlike a regular view, it doesn't re-run the
query every time — it's fast like a table. But you must REFRESH it manually to get updated data.
■ Example:
CREATE MATERIALIZED VIEW monthly_sales AS
SELECT month, SUM(amount) FROM sales GROUP BY month;
■ Memory Tip: Regular view = live TV stream. Materialized view = recorded show (faster, but
needs updating).
■ Answer:
An index is a data structure that helps PostgreSQL find rows faster — like a book index. Without it,
PostgreSQL reads every row (slow). With it, it jumps straight to the right rows. Use indexes on columns
you frequently search, filter, or join on.
■ Example:
-- Create an index on email (searched often)
CREATE INDEX idx_users_email ON users(email);
■ Memory Tip: Index = table of contents. Without it, you read the whole book to find one word.
■ Answer:
Indexes slow down INSERT, UPDATE, and DELETE because PostgreSQL must update the index too.
Avoid indexes on columns with very few unique values (like a 'gender' column with only M/F). Also avoid
on very small tables — a full scan is just as fast.
■ Example:
-- BAD index (only 2 unique values - useless)
CREATE INDEX idx_gender ON users(gender);
■ Memory Tip: Too many indexes = too many signposts — they slow you down when you're
walking (writing).
■ Answer:
B-Tree (default) works for =, <, >, BETWEEN — great for most cases. Hash is only for = comparisons,
very fast. GIN (Generalized Inverted Index) is for JSONB and full-text search. GiST is for geometric data
and text similarity.
■ Example:
-- B-Tree (default, most common)
CREATE INDEX ON products(price);
■ Memory Tip: B-Tree = jack of all trades. GIN = specialist for JSON/text search.
■ Answer:
A transaction groups multiple SQL statements into one safe unit. Either ALL statements succeed, or
NONE of them do. ACID stands for: Atomicity (all or nothing), Consistency (data rules always hold),
Isolation (transactions don't interfere), Durability (saved changes survive crashes).
■ Example:
BEGIN;
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;
COMMIT; -- both happen together
■ Memory Tip: ACID = the rules of a bank transfer. Money never disappears in transit.
■ Answer:
ROLLBACK undoes all changes in the current transaction back to the BEGIN. SAVEPOINT creates a
checkpoint inside a transaction — you can rollback to it without cancelling the whole transaction.
■ Example:
BEGIN;
INSERT INTO orders VALUES (1, 'Laptop');
SAVEPOINT after_insert;
■ Memory Tip: SAVEPOINT = a video game checkpoint — mess up, reload from there, not the
start.
■ Answer:
Normalization organizes tables to reduce duplicate data. 1NF: each cell has one value (no lists in a
column). 2NF: every non-key column depends on the WHOLE primary key. 3NF: no column depends on
another non-key column (no indirect dependencies).
■ Example:
-- BAD (not 1NF): phones column has multiple values
id | name | phones
1 | Alice | 111,222
■ Memory Tip: 1NF = one thing per box. 2NF = no partial dependency. 3NF = no indirect
dependency.
■ Answer:
A function MUST return a value. A stored procedure does NOT have to return anything. Functions can be
used inside SELECT queries. Procedures are called with CALL. From PostgreSQL 11+, procedures can
manage transactions (COMMIT/ROLLBACK inside).
■ Example:
-- Function: must return something
CREATE FUNCTION get_tax(price NUMERIC)
RETURNS NUMERIC AS $$
SELECT price * 0.18;
$$ LANGUAGE SQL;
■ Memory Tip: Function = vending machine (always gives you something). Procedure =
maintenance worker (does a job, may not give you anything).
■ Answer:
JSON stores data as plain text — it preserves exact formatting and key order. JSONB stores data in
binary format — it's faster to query and supports GIN indexes. JSONB removes duplicate keys and
doesn't preserve key order. For querying, always prefer JSONB.
■ Example:
-- Store user preferences as JSONB
CREATE TABLE users (
id SERIAL PRIMARY KEY,
prefs JSONB
);
■ Memory Tip: JSON = photo. JSONB = compressed photo. Same image, JSONB loads faster and
you can search inside it.
■ Answer:
A CTE is a temporary named result set defined at the top of a query with WITH. It makes complex
queries more readable by breaking them into named chunks. CTEs can even reference themselves
(recursive CTEs).
■ Example:
WITH high_earners AS (
SELECT id, name, salary
FROM employees
WHERE salary > 80000
)
SELECT [Link], d.dept_name
FROM high_earners h
JOIN departments d ON h.dept_id = [Link];
■ Memory Tip: CTE = give a long calculation a nickname, then use that nickname in the rest of
your query.
■ Answer:
Window functions perform calculations across rows related to the current row — without collapsing them
like GROUP BY does. Common ones: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, SUM
OVER. The OVER() clause defines the 'window'.
■ Example:
SELECT
name,
salary,
RANK() OVER (ORDER BY salary DESC) AS salary_rank,
AVG(salary) OVER () AS company_avg
FROM employees;
-- Each row still appears! No grouping.
■ Memory Tip: Window function = scoring everyone in a race while keeping each runner visible.
■ Answer:
UNION combines results from two queries and removes duplicates. UNION ALL combines results and
keeps ALL rows — including duplicates. UNION ALL is faster because it skips the duplicate-check step.
■ Example:
-- UNION: removes duplicates (slower)
SELECT city FROM customers
UNION
SELECT city FROM suppliers;
■ Memory Tip: UNION = unique names at roll call. UNION ALL = every name read aloud, even if
repeated.
■ Answer:
A subquery is a query nested inside another query. The inner query runs first and its result is used by the
outer query. You can use subqueries in SELECT, FROM, or WHERE clauses.
■ Example:
-- Subquery in WHERE
SELECT name FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees
);
■ Answer:
MVCC means PostgreSQL keeps multiple versions of the same row. When you update a row,
PostgreSQL doesn't overwrite it — it creates a NEW version and marks the old one as expired. This lets
readers and writers work at the same time without locking each other out.
■ Example:
-- Transaction A reads row (sees version 1)
-- Transaction B updates row (creates version 2)
-- Transaction A still sees version 1 (its snapshot)
-- No lock! Both run simultaneously
■ Memory Tip: MVCC = each reader gets their own snapshot of reality. No queue needed.
■ Answer:
EXPLAIN shows the query execution plan — how PostgreSQL PLANS to run your query. EXPLAIN
ANALYZE actually RUNS the query and shows real timing data. Use it to find slow operations (Seq Scan
= full table scan = slow on big tables).
■ Example:
EXPLAIN SELECT * FROM orders WHERE customer_id = 5;
-- Output shows: Seq Scan or Index Scan
■ Memory Tip: EXPLAIN = GPS route preview. EXPLAIN ANALYZE = GPS tracking your actual
drive.
■ Answer:
Isolation levels control how much one transaction can see of another's uncommitted work. READ
COMMITTED (default): see committed data only. REPEATABLE READ: data doesn't change
mid-transaction. SERIALIZABLE: strictest — transactions behave as if run one-by-one.
■ Example:
-- Set isolation level for a transaction
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE id = 1;
-- Balance won't change even if others commit
COMMIT;
■ Memory Tip: READ COMMITTED = newspaper (today's news). REPEATABLE READ = book
(same edition throughout).
■ Answer:
Partitioning splits one large table into smaller physical pieces (partitions) based on a column value — like
splitting a yearly log table into monthly chunks. PostgreSQL queries only the relevant partition, making
them much faster.
■ Example:
CREATE TABLE sales (
id SERIAL,
sale_date DATE,
amount NUMERIC
) PARTITION BY RANGE (sale_date);
■ Memory Tip: Partitioning = organizing files into yearly folders. Find 2024 data? Open the 2024
folder only.
■ Answer:
PostgreSQL is more feature-rich: supports JSONB, full ACID compliance, advanced window functions,
table inheritance, and custom types. MySQL is simpler and historically faster for read-heavy web apps.
PostgreSQL is stricter with data integrity; MySQL can silently truncate data or allow bad values by
default.
■ Example:
PostgreSQL WINS at: - JSONB (much faster than MySQL JSON) - Window functions - Full-text search -
Data types (arrays, hstore) - Strict data validation MySQL WINS at: - Simpler setup for small apps -
Slightly faster simple reads (historically) - Wider shared hosting support
■ Memory Tip: PostgreSQL = strict engineer. MySQL = friendly intern. Both good — pick based
on your needs.
■ Answer:
A trigger is a function that runs automatically when a specific event happens on a table — like INSERT,
UPDATE, or DELETE. Triggers can run BEFORE or AFTER the event. They're used for audit logging,
enforcing rules, or auto-filling columns.
■ Example:
-- Log every update to a separate table
CREATE FUNCTION log_changes() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO audit_log(table_name, action, changed_at)
VALUES (TG_TABLE_NAME, TG_OP, NOW());
RETURN NEW;
END; $$ LANGUAGE plpgsql;
Q7. What is connection pooling and why does PostgreSQL need it?
★ Importance: ■■■ Advanced
■ Answer:
PostgreSQL creates a new OS process for every connection. At scale, 1000 connections = 1000
processes = system overload. A connection pooler (like PgBouncer) sits between your app and
PostgreSQL — it reuses a small pool of real connections for thousands of app requests.
■ Example:
Without pooling:
App has 500 users → 500 PostgreSQL processes → server dies
With PgBouncer:
500 users → PgBouncer → 20 PostgreSQL connections
→ server stays healthy
■ Memory Tip: Connection pool = airport shuttle — 200 people, but only 10 vans. Smart
scheduling keeps everyone moving.
■ Answer:
Because of MVCC, dead row versions pile up over time. VACUUM reclaims that space. VACUUM
ANALYZE also updates table statistics so the query planner makes smarter decisions. Auto-vacuum runs
in the background automatically — but you can run it manually too.
■ Example:
-- Manual vacuum
VACUUM users;
■ Memory Tip: VACUUM = garbage collection. PostgreSQL hoards old row versions — VACUUM
throws them out.
■ Answer:
A recursive CTE is a CTE that references itself. It's used for hierarchical data — like an org chart
(employee → manager → their manager) or category trees. It has two parts: the anchor (starting point)
and the recursive part (next level).
■ Example:
WITH RECURSIVE org_chart AS (
-- Anchor: start with top-level (no manager)
SELECT id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
■ Memory Tip: Recursive CTE = peeling an onion layer by layer, starting from the outside.
■ Answer:
PostgreSQL converts text into searchable tokens called tsvector (a sorted list of unique stemmed words).
Your search query becomes a tsquery. The @@ operator matches them. Add a GIN index for fast search
across millions of rows.
■ Example:
-- Convert article body to search tokens
SELECT to_tsvector('english', 'Cats are great pets') ;
-- Returns: 'cat':1 'great':3 'pet':4
■ Memory Tip: Full-text search = a smart librarian who understands 'cats' and 'cat' are the same
word.
ACID: Atomicity, Consistency, Isolation, Durability — the four guarantees that keep your data safe.
PRIMARY KEY: A column (or columns) that uniquely identifies every row. Cannot be NULL. Cannot repeat.
FOREIGN KEY: A column that points to the PRIMARY KEY of another table, enforcing relational integrity.
INDEX: A data structure that speeds up SELECT queries at the cost of slower writes.
VIEW: A saved SQL query that looks like a table — no data stored, re-runs every time.
MATERIALIZED VIEW: Like a view, but results are stored on disk. Must be manually REFRESH-ed.
TRANSACTION: A group of SQL statements that all succeed or all fail together.
ROLLBACK: Undo all changes in the current transaction, going back to before BEGIN.
SAVEPOINT: A checkpoint inside a transaction you can partially roll back to.
MVCC: PostgreSQL keeps multiple versions of rows so readers and writers never block each other.
VACUUM: Cleans up dead row versions left behind by MVCC, reclaiming disk space.
JSONB: Binary JSON — faster to query than JSON, supports GIN indexes, removes duplicate keys.
CTE: A named temporary result set defined with WITH — makes complex queries readable.
Window Function: Calculates values across related rows without collapsing them — uses OVER().
3NF: Every non-key column depends on the key, the whole key, and nothing but the key.
EXPLAIN ANALYZE: Runs your query and shows the real execution plan with actual timing data.
GIN Index: Specialized index for JSONB and full-text search — fast at finding values inside complex data.
PRIMARY KEY UNIQUE constraint Primary key: one per table, never NULL. Unique:
many per table, allows one NULL.
INNER JOIN LEFT JOIN INNER: only matching rows. LEFT: all left rows +
matching right rows (NULLs for gaps).
■ 3. Must-Remember Rules
1. NULL = NULL is always NULL — never TRUE. Use IS NULL.
2. SERIAL is shorthand for SEQUENCE — PostgreSQL creates a sequence object behind the scenes.
4. FOREIGN KEY prevents orphan records — you cannot reference a row that doesn't exist.
5. B-Tree index is the default and works for =, <, >, BETWEEN, LIKE 'prefix%'.
6. VACUUM is essential in heavy write workloads — auto-vacuum handles it, but know it exists.
7. Window functions use OVER() and do NOT collapse rows (unlike GROUP BY + aggregate).
8. EXPLAIN ANALYZE runs the query for real — never use it on a slow query against production without
caution.
9. ROLLBACK only works inside an active transaction (between BEGIN and COMMIT).
10. Materialized views must be REFRESH-ed manually — they don't auto-update when base data changes.
You've reviewed the whole guide. Now close it, take a deep breath, and
trust yourself. ■