PostgreSQL (PostgreSQL) SQL Cheat Sheet
Connection & Basic Commands
Connect to Database
-- Connect to PostgreSQL
psql -U username -d database_name
psql -h hostname -p port -U username -d database_name
-- Connect to database with SSL
psql "sslmode=require host=hostname dbname=database_name user=username"
Basic psql Commands
-- List all databases
\l
-- Connect to specific database
\c database_name
-- List all tables
\dt
-- Describe table structure
\d table_name
\d+ table_name -- with more details
-- List all schemas
\dn
-- List all users
\du
-- List all views
\dv
-- List all functions
\df
-- Show current database
SELECT current_database();
-- Show current user
SELECT current_user;
-- Show PostgreSQL version
SELECT version();
-- Quit psql
\q
Database Operations
Create Database
CREATE DATABASE myapp_db;
CREATE DATABASE myapp_db WITH OWNER admin;
CREATE DATABASE myapp_db WITH ENCODING 'UTF8';
Drop Database
DROP DATABASE IF EXISTS myapp_db;
Alter Database
ALTER DATABASE old_name RENAME TO new_name;
ALTER DATABASE myapp_db OWNER TO new_owner;
Table Operations
Create Table
-- Basic table creation
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE,
age INTEGER CHECK (age >= 18),
salary DECIMAL(10,2) DEFAULT 0.00,
hire_date DATE NOT NULL DEFAULT CURRENT_DATE,
department_id INTEGER REFERENCES departments(id)
);
-- Create temporary table
CREATE TEMP TABLE temp_data (
id INTEGER,
value TEXT
);
-- Create table from query
CREATE TABLE employees_backup AS
SELECT * FROM employees WHERE hire_date < '2020-01-01';
Alter Table
-- Add column
ALTER TABLE employees ADD COLUMN phone VARCHAR(20);
-- Drop column
ALTER TABLE employees DROP COLUMN phone;
-- Modify column type
ALTER TABLE employees ALTER COLUMN salary TYPE NUMERIC(12,2);
-- Add constraint
ALTER TABLE employees ADD CONSTRAINT check_age CHECK (age >= 18);
-- Drop constraint
ALTER TABLE employees DROP CONSTRAINT check_age;
-- Rename table
ALTER TABLE employees RENAME TO staff;
-- Rename column
ALTER TABLE employees RENAME COLUMN name TO full_name;
Drop Table
DROP TABLE employees;
DROP TABLE IF EXISTS employees CASCADE;
Data Manipulation
INSERT
-- Insert single row
INSERT INTO employees (name, email, age, salary)
VALUES ('John Doe', '
[email protected]', 30, 50000.00);
-- Insert multiple rows
INSERT INTO employees (name, email, age, salary) VALUES
('Jane Smith', '
[email protected]', 28, 55000.00),
('Bob Johnson', '
[email protected]', 35, 60000.00),
('Alice Brown', '
[email protected]', 32, 52000.00);
-- Insert from another table
INSERT INTO employees_backup
SELECT * FROM employees WHERE department_id = 1;
-- Insert with ON CONFLICT (UPSERT)
INSERT INTO employees (id, name, email, salary)
VALUES (1, 'John Doe', '
[email protected]', 55000.00)
ON CONFLICT (id)
DO UPDATE SET salary = EXCLUDED.salary;
SELECT
-- Basic select
SELECT * FROM employees;
SELECT name, email, salary FROM employees;
-- With WHERE clause
SELECT * FROM employees WHERE age > 30;
SELECT * FROM employees WHERE salary BETWEEN 40000 AND 60000;
SELECT * FROM employees WHERE name LIKE 'John%';
SELECT * FROM employees WHERE email ILIKE '%gmail.com';
-- Sorting
SELECT * FROM employees ORDER BY salary DESC;
SELECT * FROM employees ORDER BY age ASC, salary DESC;
-- Limiting results
SELECT * FROM employees LIMIT 10;
SELECT * FROM employees LIMIT 10 OFFSET 20;
-- Distinct values
SELECT DISTINCT department_id FROM employees;
-- Conditional selection
SELECT name,
CASE
WHEN salary > 60000 THEN 'High'
WHEN salary > 40000 THEN 'Medium'
ELSE 'Low'
END as salary_grade
FROM employees;
UPDATE
-- Update single row
UPDATE employees
SET salary = 65000
WHERE id = 1;
-- Update multiple columns
UPDATE employees
SET salary = salary * 1.1,
hire_date = CURRENT_DATE
WHERE department_id = 2;
-- Update with subquery
UPDATE employees
SET salary = (SELECT AVG(salary) FROM employees WHERE department_id = 1)
WHERE id = 5;
-- Update with JOIN
UPDATE employees
SET salary = employees.salary + departments.bonus
FROM departments
WHERE employees.department_id = departments.id;
DELETE
-- Delete specific rows
DELETE FROM employees WHERE age < 18;
-- Delete with subquery
DELETE FROM employees
WHERE id IN (SELECT id FROM employees WHERE salary < 30000);
-- Delete all rows (but keep table structure)
DELETE FROM employees;
-- Truncate (faster way to delete all rows)
TRUNCATE TABLE employees;
TRUNCATE TABLE employees RESTART IDENTITY CASCADE;
Joins
INNER JOIN
SELECT e.name, e.salary, d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
LEFT JOIN
SELECT e.name, e.salary, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
RIGHT JOIN
SELECT e.name, e.salary, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
FULL OUTER JOIN
SELECT e.name, e.salary, d.department_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;
Self JOIN
SELECT e1.name as employee, e2.name as manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id;
Multiple JOINs
SELECT e.name, d.department_name, p.project_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id
INNER JOIN employee_projects ep ON e.id = ep.employee_id
INNER JOIN projects p ON ep.project_id = p.id;
Aggregate Functions
Basic Aggregates
-- Count records
SELECT COUNT(*) FROM employees;
SELECT COUNT(DISTINCT department_id) FROM employees;
-- Sum values
SELECT SUM(salary) FROM employees;
SELECT SUM(salary) FROM employees WHERE department_id = 1;
-- Average
SELECT AVG(salary) FROM employees;
SELECT ROUND(AVG(salary), 2) FROM employees;
-- Min/Max
SELECT MIN(salary), MAX(salary) FROM employees;
SELECT MIN(hire_date), MAX(hire_date) FROM employees;
GROUP BY
-- Group by single column
SELECT department_id, COUNT(*), AVG(salary)
FROM employees
GROUP BY department_id;
-- Group by multiple columns
SELECT department_id, EXTRACT(YEAR FROM hire_date) as hire_year, COUNT(*)
FROM employees
GROUP BY department_id, EXTRACT(YEAR FROM hire_date);
-- Having clause
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 50000;
Subqueries
Scalar Subqueries
-- Single value subquery
SELECT name, salary,
(SELECT AVG(salary) FROM employees) as avg_salary
FROM employees;
IN Subqueries
-- Multiple values subquery
SELECT * FROM employees
WHERE department_id IN (
SELECT id FROM departments WHERE location = 'New York'
);
EXISTS Subqueries
-- Check existence
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.id = e.department_id AND d.budget > 100000
);
Correlated Subqueries
-- Subquery references outer query
SELECT * FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
Window Functions
ROW_NUMBER, RANK, DENSE_RANK
-- Row numbering
SELECT name, salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) as row_num,
RANK() OVER (ORDER BY salary DESC) as rank,
DENSE_RANK() OVER (ORDER BY salary DESC) as dense_rank
FROM employees;
PARTITION BY
-- Partition window functions
SELECT name, department_id, salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) as dept_rank,
AVG(salary) OVER (PARTITION BY department_id) as dept_avg_salary
FROM employees;
LAG and LEAD
-- Access previous/next row values
SELECT name, salary,
LAG(salary) OVER (ORDER BY hire_date) as prev_salary,
LEAD(salary) OVER (ORDER BY hire_date) as next_salary
FROM employees;
Running Totals
-- Cumulative calculations
SELECT name, salary,
SUM(salary) OVER (ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT
AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as
FROM employees;
Indexes
Create Indexes
-- Basic index
CREATE INDEX idx_employee_name ON employees(name);
-- Composite index
CREATE INDEX idx_employee_dept_salary ON employees(department_id, salary);
-- Unique index
CREATE UNIQUE INDEX idx_employee_email ON employees(email);
-- Partial index
CREATE INDEX idx_high_salary ON employees(salary) WHERE salary > 50000;
-- Expression index
CREATE INDEX idx_lower_name ON employees(LOWER(name));
-- Hash index
CREATE INDEX idx_employee_id ON employees USING HASH(id);
Drop Indexes
DROP INDEX idx_employee_name;
DROP INDEX IF EXISTS idx_employee_email;
Constraints
Primary Key
-- Add primary key
ALTER TABLE employees ADD CONSTRAINT pk_employees PRIMARY KEY (id);
-- Composite primary key
ALTER TABLE employee_projects ADD CONSTRAINT pk_emp_proj PRIMARY KEY (employee_id, projec
Foreign Key
-- Add foreign key
ALTER TABLE employees
ADD CONSTRAINT fk_employees_department
FOREIGN KEY (department_id) REFERENCES departments(id);
-- Foreign key with actions
ALTER TABLE employees
ADD CONSTRAINT fk_employees_department
FOREIGN KEY (department_id) REFERENCES departments(id)
ON DELETE SET NULL ON UPDATE CASCADE;
Check Constraints
-- Add check constraint
ALTER TABLE employees ADD CONSTRAINT chk_age CHECK (age >= 18 AND age <= 65);
ALTER TABLE employees ADD CONSTRAINT chk_salary CHECK (salary > 0);
Unique Constraints
-- Add unique constraint
ALTER TABLE employees ADD CONSTRAINT uk_employee_email UNIQUE (email);
NOT NULL Constraints
-- Add not null constraint
ALTER TABLE employees ALTER COLUMN name SET NOT NULL;
ALTER TABLE employees ALTER COLUMN email SET NOT NULL;
Data Types
Numeric Types
-- Integer types
CREATE TABLE numbers (
small_int SMALLINT, -- 2 bytes
regular_int INTEGER, -- 4 bytes
big_int BIGINT, -- 8 bytes
auto_id SERIAL, -- auto-incrementing integer
decimal_val DECIMAL(10,2), -- exact decimal
float_val REAL, -- 4 bytes floating point
double_val DOUBLE PRECISION -- 8 bytes floating point
);
String Types
-- Character types
CREATE TABLE strings (
fixed_char CHAR(10), -- fixed length
var_char VARCHAR(255), -- variable length with limit
unlimited_text TEXT -- unlimited length
);
Date/Time Types
-- Date and time types
CREATE TABLE dates (
just_date DATE,
just_time TIME,
date_time TIMESTAMP,
date_time_tz TIMESTAMPTZ,
time_interval INTERVAL
);
-- Date/time examples
INSERT INTO dates VALUES (
'2024-01-15',
'14:30:00',
'2024-01-15 14:30:00',
'2024-01-15 14:30:00+00',
'2 days 3 hours'
);
Boolean and Other Types
-- Various types
CREATE TABLE misc_types (
is_active BOOLEAN,
data_json JSON,
data_jsonb JSONB,
unique_id UUID,
ip_address INET,
file_path BYTEA
);
JSON Operations
JSON Queries
-- JSON column operations
SELECT name, profile->>'age' as age
FROM users
WHERE profile->>'city' = 'New York';
-- JSON array operations
SELECT name, hobbies->>0 as first_hobby
FROM users
WHERE JSON_ARRAY_LENGTH(hobbies) > 2;
-- JSONB operations (more efficient)
SELECT * FROM products
WHERE specs @> '{"color": "red"}';
-- JSON path queries
SELECT name, jsonb_path_query(profile, '$.address.city') as city
FROM users;
Common Table Expressions (CTEs)
Basic CTE
-- Simple CTE
WITH high_earners AS (
SELECT * FROM employees WHERE salary > 60000
)
SELECT department_id, COUNT(*) as high_earner_count
FROM high_earners
GROUP BY department_id;
Recursive CTE
-- Recursive CTE for hierarchical data
WITH RECURSIVE employee_hierarchy AS (
-- Base case: top-level managers
SELECT id, name, manager_id, 0 as level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Recursive case: employees with managers
SELECT e.id, e.name, e.manager_id, eh.level + 1
FROM employees e
INNER JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT * FROM employee_hierarchy ORDER BY level, name;
Views
Create Views
-- Simple view
CREATE VIEW employee_summary AS
SELECT name, email, salary, department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
-- Materialized view (stores results physically)
CREATE MATERIALIZED VIEW monthly_sales AS
SELECT DATE_TRUNC('month', sale_date) as month,
SUM(amount) as total_sales
FROM sales
GROUP BY DATE_TRUNC('month', sale_date);
-- Refresh materialized view
REFRESH MATERIALIZED VIEW monthly_sales;
Drop Views
DROP VIEW employee_summary;
DROP MATERIALIZED VIEW monthly_sales;
Functions and Procedures
Built-in Functions
-- String functions
SELECT UPPER(name), LOWER(email), LENGTH(name) FROM employees;
SELECT CONCAT(first_name, ' ', last_name) as full_name FROM employees;
SELECT SUBSTRING(email FROM 1 FOR POSITION('@' IN email) - 1) as username FROM employees;
-- Date functions
SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP;
SELECT EXTRACT(YEAR FROM hire_date) as hire_year FROM employees;
SELECT AGE(CURRENT_DATE, hire_date) as employment_duration FROM employees;
-- Math functions
SELECT ROUND(salary/12, 2) as monthly_salary FROM employees;
SELECT RANDOM(), FLOOR(RANDOM() * 100) as random_number;
User-Defined Functions
-- Create function
CREATE OR REPLACE FUNCTION calculate_bonus(emp_salary DECIMAL)
RETURNS DECIMAL AS $$
BEGIN
IF emp_salary > 60000 THEN
RETURN emp_salary * 0.1;
ELSE
RETURN emp_salary * 0.05;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Use function
SELECT name, salary, calculate_bonus(salary) as bonus FROM employees;
Transactions
Transaction Control
-- Begin transaction
BEGIN;
-- Insert some data
INSERT INTO employees (name, email, salary) VALUES ('Test User', '
[email protected]', 45000)
-- Update data
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 1;
-- Commit changes
COMMIT;
-- Rollback if needed
BEGIN;
DELETE FROM employees WHERE id = 999;
ROLLBACK; -- Undo the delete
Savepoints
BEGIN;
INSERT INTO employees (name, email, salary) VALUES ('User 1', '[email protected]', 45000);
SAVEPOINT sp1;
INSERT INTO employees (name, email, salary) VALUES ('User 2', '[email protected]', 50000);
ROLLBACK TO sp1; -- Rollback to savepoint
INSERT INTO employees (name, email, salary) VALUES ('User 3', '
[email protected]', 48000);
COMMIT;
This comprehensive PostgreSQL cheat sheet covers the most commonly used commands and
operations [1] [2] [3] [4] [5] [6] [7] . Each section provides practical examples that you can use
directly in your PostgreSQL database. The examples demonstrate real-world usage patterns for
database operations [8] [9] [10] [11] [12] , joins [13] [14] [15] [16] , aggregate functions [17] [18] , window
functions [19] [20] , subqueries [21] [22] , constraints [23] [24] , and many other essential PostgreSQL
features [25] [26] .
⁂
1. https://hasura.io/blog/top-psql-commands-and-flags-you-need-to-know-postgresql
2. https://www.bytebase.com/reference/postgres/how-to/top-psql-commands-with-examples/
3. https://neon.com/postgresql/postgresql-cheat-sheet
4. https://www.commandprompt.com/education/postgresql-basic-psql-commands/
5. https://www.tutorialspoint.com/postgresql/postgresql_syntax.htm
6. https://quickref.me/postgres.html
7. https://knowledgebase.censhare.com/tk/most-useful-postgresql-commands
8. https://phoenixnap.com/kb/postgresql-select
9. https://www.tutorialspoint.com/postgresql/postgresql_select_query.htm
10. https://www.datacamp.com/doc/postgresql/select
11. https://www.enterprisedb.com/postgres-tutorials/how-modify-data-postgresql-using-insert-update-up
date-joins-delete-and-upsert
12. https://www.tutorialspoint.com/postgresql/postgresql_create_table.htm
13. https://www.w3schools.com/postgresql/postgresql_joins.php
14. https://www.devart.com/dbforge/postgresql/studio/postgresql-joins.html
15. https://tembo.io/docs/getting-started/postgres_guides/all-possible-joins-in-postgres
16. https://www.prisma.io/dataguide/postgresql/reading-and-querying-data/joining-tables
17. https://neon.com/postgresql/postgresql-aggregate-functions
18. https://www.commandprompt.com/education/postgresql-aggregate-functions-with-practical-example
s/
19. https://coderpad.io/blog/development/window-functions-aggregate-data-postgres/
20. https://www.timescale.com/learn/postgresql-window-functions
21. https://www.datacamp.com/doc/postgresql/subqueries
22. https://neon.com/postgresql/postgresql-tutorial/postgresql-subquery
23. https://www.navicat.com/en/company/aboutus/blog/2423-exploring-different-types-of-constraints-in-
postgresql.html
24. https://www.prisma.io/dataguide/postgresql/column-and-table-constraints
25. https://www.postgresql.org/docs/current/indexes-types.html
26. https://devcenter.heroku.com/articles/postgresql-indexes