Some links in this article are affiliate links. We earn a commission at no extra cost to you when you purchase through them. Full disclosure.
Click any command to expand the explanation and examples. For standard SQL syntax, see the SQL cheat sheet. Choosing a database? Read Postgres vs SQLite vs MySQL.
π₯οΈ psql Commands
Connect to a database psql
# Connect psql -U username -d database_name psql -h localhost -p 5432 -U username -d mydbConnection string
psql βpostgresql://user:pass@localhost:5432/mydbβ
Connect to default database
psql -U postgres
\l \dt \d β list databases, tables, columns psql
\l -- List all databases \c mydb -- Connect to database \dt -- List tables in current schema \dt+ -- List tables with sizes \d users -- Describe table (columns, types, indexes) \di -- List indexes \dv -- List views \df -- List functions \dn -- List schemas \du -- List roles/users \dx -- List extensions
psql utility commands psql
\q -- Quit \? -- Help for psql commands \h SELECT -- Help for SQL commands \timing -- Toggle query timing \x -- Toggle expanded display (vertical output) \e -- Open query in editor \i file.sql -- Execute SQL file \o out.txt -- Send output to file \! ls -- Run shell command
π Data Types
Common data types types
-- Numbers INTEGER (INT) -- -2B to 2B BIGINT -- -9Q to 9Q SERIAL -- Auto-increment integer BIGSERIAL -- Auto-increment bigint NUMERIC(10,2) -- Exact decimal (money, etc.) REAL -- 6 decimal digits DOUBLE PRECISION -- 15 decimal digitsUseβ Text VARCHAR(255) β Variable length, max 255 TEXT β Unlimited length CHAR(10) β Fixed length, padded
β Date/Time DATE β 2026-03-14 TIME β 14:30:00 TIMESTAMP β 2026-03-14 14:30:00 TIMESTAMPTZ β With timezone (preferred!) INTERVAL β Time span
β Other BOOLEAN β true/false UUID β gen_random_uuid() JSONB β Binary JSON (preferred over JSON) ARRAY β INTEGER[], TEXT[] BYTEA β Binary data INET β IP address
TIMESTAMPTZ over TIMESTAMP and JSONB over JSON β almost always.
π CRUD Operations
CREATE TABLE ddl
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name TEXT NOT NULL,
role VARCHAR(50) DEFAULT 'user',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
β With foreign key
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
body TEXT,
published BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW()
);
INSERT dml
-- Single row
INSERT INTO users (email, name)
VALUES ('alice@example.com', 'Alice');
β Multiple rows
INSERT INTO users (email, name) VALUES
(βbob@example.comβ, βBobβ),
(βcarol@example.comβ, βCarolβ);
β Insert and return the new row
INSERT INTO users (email, name)
VALUES (βdave@example.comβ, βDaveβ)
RETURNING id, email;
β Insert or update (upsert)
INSERT INTO users (email, name)
VALUES (βalice@example.comβ, βAlice Updatedβ)
ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;
SELECT β queries dml
-- Basics SELECT * FROM users WHERE role = 'admin'; SELECT name, email FROM users ORDER BY created_at DESC; SELECT * FROM users LIMIT 10 OFFSET 20;β Aggregates SELECT role, COUNT() FROM users GROUP BY role; SELECT role, COUNT() FROM users GROUP BY role HAVING COUNT(*) > 5;
β Joins SELECT u.name, p.title FROM users u JOIN posts p ON p.user_id = u.id WHERE p.published = true;
β Left join (include users with no posts) SELECT u.name, COUNT(p.id) as post_count FROM users u LEFT JOIN posts p ON p.user_id = u.id GROUP BY u.name;
β Subquery SELECT * FROM users WHERE id IN (SELECT user_id FROM posts WHERE published = true);
β CTE (Common Table Expression) WITH active_users AS ( SELECT * FROM users WHERE last_login > NOW() - INTERVAL β30 daysβ ) SELECT * FROM active_users WHERE role = βadminβ;
UPDATE and DELETE dml
-- Update UPDATE users SET name = 'Alice B.' WHERE email = 'alice@example.com'; UPDATE users SET role = 'admin' WHERE id IN (1, 2, 3);β Update with RETURNING UPDATE users SET role = βadminβ WHERE id = 1 RETURNING *;
β Delete DELETE FROM users WHERE id = 5; DELETE FROM users WHERE last_login < NOW() - INTERVAL β1 yearβ;
β Truncate (delete all rows, fast) TRUNCATE TABLE logs;
π Indexes & Performance
CREATE INDEX index
-- B-tree (default, good for =, <, >, BETWEEN) CREATE INDEX idx_users_email ON users(email);β Unique index CREATE UNIQUE INDEX idx_users_email ON users(email);
β Composite index CREATE INDEX idx_posts_user_published ON posts(user_id, published);
β Partial index (only index some rows) CREATE INDEX idx_active_users ON users(email) WHERE active = true;
β GIN index (for JSONB, arrays, full-text) CREATE INDEX idx_users_metadata ON users USING GIN(metadata);
β Drop index DROP INDEX idx_users_email;
EXPLAIN ANALYZE perf
-- See query plan EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';β See actual execution time EXPLAIN ANALYZE SELECT * FROM users WHERE email = βalice@example.comβ;
β Look for: β Seq Scan = full table scan (slow on big tables) β Index Scan = using an index (good) β Nested Loop = might be slow with many rows
π§© JSONB Operations
Query and update JSONB json
-- Access a key SELECT metadata->>'theme' FROM users; -- as text SELECT metadata->'settings' FROM users; -- as JSONβ Nested access SELECT metadata->βsettingsβ->>βlanguageβ FROM users;
β Filter by JSON value SELECT * FROM users WHERE metadata->>βroleβ = βadminβ;
β Check if key exists SELECT * FROM users WHERE metadata ? βthemeβ;
β Contains SELECT * FROM users WHERE metadata @> β{βroleβ: βadminβ}β;
β Update a key UPDATE users SET metadata = metadata || β{βthemeβ: βdarkβ}β WHERE id = 1;
β Remove a key UPDATE users SET metadata = metadata - βthemeβ WHERE id = 1;
β‘ Useful Patterns
Backup and restore admin
# Backup pg_dump -U postgres mydb > backup.sql pg_dump -U postgres -Fc mydb > backup.dump # Custom format (compressed)Restore
psql -U postgres mydb < backup.sql pg_restore -U postgres -d mydb backup.dump
Backup specific table
pg_dump -U postgres -t users mydb > users.sql
ALTER TABLE ddl
-- Add column ALTER TABLE users ADD COLUMN bio TEXT;β Drop column ALTER TABLE users DROP COLUMN bio;
β Rename column ALTER TABLE users RENAME COLUMN name TO full_name;
β Change type ALTER TABLE users ALTER COLUMN name TYPE VARCHAR(500);
β Add constraint ALTER TABLE users ADD CONSTRAINT email_check CHECK (email LIKE β%@%β);
β Rename table ALTER TABLE users RENAME TO accounts;
Quick access: Raycast lets you search commands, snippets, and cheat sheets instantly from your keyboard. Free for Mac.
Related: PostgreSQL vs MySQL Β· PostgreSQL vs SQLite Β· Code Review Sql Query Β· Mongodb Was A Mistake