You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A clean, well-organized, exhaustive reference for developers, DBAs, and interview preparation.
Covers everything from basic connection to advanced features like JSONB, partitioning, extensions, and performance tuning.
1. Connecting & psql Basics
Command
Description
psql -U user -d dbname
Connect to database
psql -h host -p 5432 -U user -d db
Connect with host/port
psql "postgres://user:pass@host:5432/db"
Connection string (URI)
\l
List databases
\c dbname
Switch database
\du
List roles/users
\dt
List tables
\d name
Describe table/view/index/sequence
\d+ name
Describe with size & description
\di / \dv / \df / \ds
List indexes / views / functions / sequences
\x
Toggle expanded display
\?
Help for psql commands
\q
Quit psql
2. Database Operations
CREATEDATABASEmydb;
CREATEDATABASEmydb WITH OWNER = admin ENCODING ='UTF8';
DROPDATABASE mydb;
ALTERDATABASE old_name RENAME TO new_name;
ALTERDATABASE mydb OWNER TO new_owner;
3. Roles & Privileges
-- Create login user
CREATE ROLE ben WITH LOGIN PASSWORD 'secret';
-- Create non-login role
CREATE ROLE readonly;
-- Grant privilegesGRANT CONNECT ON DATABASE mydb TO ben;
GRANT USAGE ON SCHEMA public TO ben;
GRANTSELECT, INSERT, UPDATE, DELETEON ALL TABLES IN SCHEMA public TO ben;
GRANT ALL PRIVILEGES ON DATABASE mydb TO admin;
-- Default privileges for future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANTSELECTON TABLES TO readonly;
-- RevokeREVOKE ALL ON DATABASE mydb FROM ben;
-- Change passwordALTERUSER ben WITH PASSWORD 'newpass';
4. Schema Management
Command
Purpose
CREATE SCHEMA sales;
Create a new namespace for tables/views/functions
DROP SCHEMA sales CASCADE;
Delete schema and everything inside it
ALTER SCHEMA sales RENAME TO marketing;
Rename schema
SET search_path TO marketing, public;
Change default schema search order (affects unqualified table names)
5. Table Operations
Create Table
CREATETABLEusers (
id BIGSERIALPRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name TEXT,
is_active BOOLEAN DEFAULT true,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
Common ALTER TABLE Commands
Command
Explanation
CREATE TABLE users (id BIGSERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL);
Creates table with auto-increment ID and unique email
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
Add a new column
ALTER TABLE users DROP COLUMN phone;
Remove a column
ALTER TABLE users RENAME COLUMN name TO full_name;
Rename column
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
Add NOT NULL constraint
ALTER TABLE users ADD CONSTRAINT positive_balance CHECK (balance >= 0);
Add check constraint
TRUNCATE TABLE users RESTART IDENTITY CASCADE;
Empty table, reset auto-increment, and cascade to foreign keys
INSERT INTO users (email, name) VALUES ('a@x.com', 'Alice');
Multi INSERT
INSERT INTO users (email) VALUES ('a@x.com'), ('b@x.com');
UPSERT (16+)
INSERT INTO users (id, email) VALUES (1,'a@x.com') ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email;
UPDATE
UPDATE users SET is_active = false, updated_at = NOW() WHERE id = 1;
DELETE
DELETE FROM users WHERE is_active = false;
Returning
DELETE FROM users WHERE id = 1 RETURNING *;
7. SELECT Mastery
SELECT name, email FROM users
WHERE is_active = true
AND created_at >='2025-01-01'ORDER BY created_at DESCLIMIT50 OFFSET 100;
-- Distinct onSELECT DISTINCTON (category_id) *FROM products ORDER BY category_id, price DESC;
-- CaseSELECT name,
CASE WHEN age <18 THEN 'Minor'
WHEN age <65 THEN 'Adult'
ELSE 'Senior' END AS age_group
FROM users;
8. JOIN Types
Join Type
Keeps
Use Case
INNER JOIN
Only matching rows
Default when you want exact matches
LEFT JOIN
All rows from left table
Include all customers even if no orders
RIGHT JOIN
All rows from right table
Rare
FULL OUTER JOIN
All rows from both
Find mismatches in both directions
CROSS JOIN
Cartesian product
Generating test data, combinations
LATERAL JOIN
Subquery per row (like a loop)
Get latest order per user
9. Aggregations & Window Functions
SELECT
department,
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department
HAVINGCOUNT(*) >5ORDER BY avg_salary DESC;
-- Window functionsSELECT
name, salary, department,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_in_dept,
NTILE(4) OVER (ORDER BY salary DESC) AS quartile
FROM employees;
10. Indexing Strategies
CREATEINDEXidx_users_emailON users(email);
CREATEUNIQUE INDEXidx_users_email_uniqueON users(LOWER(email));
-- CompositeCREATEINDEXidx_orders_user_createdON orders(customer_id, created_at DESC);
-- PartialCREATEINDEXidx_users_activeON users(email) WHERE is_active = true;
-- GIN for JSONB / arraysCREATEINDEXidx_users_metadataON users USING GIN (metadata);
-- BRIN for large sorted tablesCREATEINDEXidx_logs_timestampON logs USING BRIN(created_at);
Drop: DROP INDEX CONCURRENTLY idx_name;
11. Views & Materialized Views
-- Regular viewCREATE OR REPLACEVIEWactive_customersASSELECT*FROM customers WHERE is_active = true;
-- Materialized view
CREATE MATERIALIZED VIEW daily_sales ASSELECT date_trunc('day', created_at)::dateAS day, SUM(amount) AS total
FROM orders
GROUP BY1;
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales;
12. JSON / JSONB Power Tools
-- QuerySELECT data->>'name'AS name, data->'specs'->>'cpu'AS cpu FROM products;
-- ContainmentSELECT*FROM products WHERE data @>'{"brand": "Apple"}';
-- Key existenceSELECT*FROM products WHERE data ? 'discount';
-- Path query (JSONB)SELECT jsonb_path_query(data, '$.specs.memory') FROM products;
-- Update JSONB fieldUPDATE products SET data = jsonb_set(data, '{price}', '999') WHERE id =1;
UPDATE products SET data = data ||'{"in_stock": false}'WHERE id =1;
13. Transactions & Savepoints
BEGIN;
UPDATE accounts SET balance = balance -100WHERE id =1;
SAVEPOINT withdraw;
UPDATE accounts SET balance = balance +100WHERE id =2;
-- OopsROLLBACK TO withdraw;
COMMIT;
14. Functions & Stored Procedures (PL/pgSQL)
CREATE OR REPLACEFUNCTIONcalculate_tax(subtotal NUMERIC)
RETURNS NUMERICAS $$
BEGIN
RETURN subtotal *0.08;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- Procedure (PostgreSQL 11+)
CREATE PROCEDURE nightly_cleanup()
LANGUAGE SQL
AS $$
DELETEFROM logs WHERE created_at < NOW() - INTERVAL '30 days';
$$;
CALL nightly_cleanup();
15. Useful Extensions
Extension
Install Command
Purpose
pg_stat_statements
CREATE EXTENSION pg_stat_statements;
Query performance monitoring
uuid-ossp
CREATE EXTENSION "uuid-ossp";
uuid_generate_v4()
postgis
CREATE EXTENSION postgis;
Geographic/spatial data
pgcrypto
CREATE EXTENSION pgcrypto;
gen_random_uuid(), crypt()
unaccent
CREATE EXTENSION unaccent;
Accent-insensitive search
pg_trgm
CREATE EXTENSION pg_trgm;
Trigram similarity (% operator)
List installed: \dx
16. Backup & Restore
# Full DB dump
pg_dump -U user -Fc dbname > db.dump
# SQL format
pg_dump -U user dbname > backup.sql
# Single table
pg_dump -t users dbname > users.sql
# Restore
pg_restore -U user -d dbname --clean --create db.dump
psql -U user -d dbname < backup.sql
Compressed: pg_dump dbname | gzip > backup.sql.gz
17. Performance & Monitoring
EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT ...;
EXPLAIN (FORMAT JSON) SELECT ...;
-- Current activitySELECT pid, query, state, query_start, now() - query_start AS duration
FROM pg_stat_activity
WHERE state ='active';
-- Terminate querySELECT pg_terminate_backend(1234);
-- Cancel query (softer)SELECT pg_cancel_backend(1234);
-- Index usageSELECT*FROM pg_stat_user_indexes ORDER BY idx_scan ASC;