cheatsheets/postgresql

PostgreSQL Commands

psql is the command-line client that ships with PostgreSQL. This sheet lists the psql meta commands and SQL you type most, from connecting to backups and killing a stuck query, with a worked example under every table.

Updated Oct 12, 2026Tested against PostgreSQL 18.6, psql 18.6, pg_dump 18.6

Also works with Neon, YugabyteDB.

Connecting

Default port is 5432. Build a URL for your host with the PostgreSQL connection string generator.

CommandWhat it does
Connect over the local socket as your OS user, to the same-named database
Connect with host, port, user and database
Force a password prompt before connecting
Connect with a connection URL
Require TLS, prompt for the password
Key-value form, verify the server certificate
Pass the password for one command
Defaults for psql, pg_dump and every libpq tool
Save a password so psql stops asking
Required, psql ignores a readable .pgpass
Run one statement and exit
Bare values, no headers or padding, for scripts
Check the server accepts connections
Installed client version

Example: connect over verified TLS with the password stored in instead of your shell history.

bash
echo 'db.example.com:5432:shop:app:s3cret' >> ~/.pgpass
chmod 600 ~/.pgpass
psql "host=db.example.com dbname=shop user=app sslmode=verify-full sslrootcert=ca.pem"

psql Meta Commands

Backslash commands run in psql only, not in other clients. Start psql with to print the SQL behind each one.

CommandWhat it does
List databases
Switch to another database
Switch database and user
Current database, user, host, port and TLS details
List tables in the search path
List tables in one schema
Tables with size and description
Describe a table: columns, indexes, foreign keys
Same plus storage and comments
List schemas
List users and roles with their attributes
List indexes, views, sequences, functions
List installed extensions
Grants on a table
Print a function's source
Expanded rows only when they are too wide
Print long results without the pager
Show NULL as visible text
Print how long each statement took
Run one query with expanded output
Rerun the query every 2 seconds
Edit the last query in
SQL syntax help for a statement
Help for every meta command
Quit

Outside psql, replaces and replaces .

Example: a psql session that finds the orders table, checks its size and watches new rows arrive.

sql
\c shop
\dt+ orders
\d orders
\timing on
SELECT count(*) FROM orders WHERE created_at > now() - interval '1 hour' \watch 5

Databases and Schemas

CommandWhat it does
Create a database owned by a role
Copy a database; nobody may be connected to shop
Same as CREATE DATABASE, from the shell
Rename; fails while others are connected
Change the owner
Size of one database
DestructiveDelete a database and every table in it
DestructiveDisconnect everyone, then delete
Which database you are in
Create a schema owned by a role
Resolve unqualified names in this order, this session
Same, for every new session of a role
Current schema lookup order
Move a table to another schema
DestructiveDelete a schema and everything in it

Example: make a scratch copy of the shop database for a risky migration, then drop it.

sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE datname = 'shop' AND pid <> pg_backend_pid();       -- template must be idle
CREATE DATABASE shop_migration_test TEMPLATE shop OWNER app;
DROP DATABASE shop_migration_test WITH (FORCE);           -- when you are done

Users, Roles and Grants

A user is a role with LOGIN. Since PostgreSQL 15, ordinary users cannot create tables in the public schema until you grant CREATE.

CommandWhat it does
Create a role that can log in
Create a group role to hold grants
Change a password; psql sends it hashed
Change a password in SQL
Let a role create databases
Expire the password on a date
Allow connecting to a database
Allow seeing objects in a schema
Allow creating tables in a schema
Read every existing table
Grant on one table
Needed for inserts into serial columns
Also cover tables app creates later
Add a user to a group role
Read every table in every schema (14+)
Take a privilege away
Fix "must be owner of table" for ALTER and DROP
Hand all objects to another role
DestructiveDrop its remaining objects and grants
Delete a role once it owns nothing
Act as another role to test grants
Back to your login role
List users outside psql

Example: a read-only reporting user for the shop database that also sees tables added later.

sql
CREATE ROLE readonly NOLOGIN;
GRANT CONNECT ON DATABASE shop TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
CREATE USER metabase WITH PASSWORD 'change-me' IN ROLE readonly;

Tables

CommandWhat it does
Create only if missing
Empty copy with defaults, indexes and constraints
New table from a query
Add a column with a default
Add only if missing
DestructiveRemove a column and its data
Rename a column
Rename a table
Change a type; fails if a view uses the column
Change a type with an explicit cast
Set a default for new rows
Forbid NULLs; scans the table
Add a check constraint
Add a foreign key
Remove a constraint
Description shown by
DestructiveDelete every row instantly
DestructiveAlso empties tables that reference it
DestructiveDelete the table

Example: the orders table used on this page, with an identity key, a foreign key and a check.

sql
CREATE TABLE orders (
  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  user_id    bigint NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  total      numeric(10,2) NOT NULL CHECK (total >= 0),
  status     text NOT NULL DEFAULT 'pending',
  created_at timestamptz NOT NULL DEFAULT now()
);

Queries

Format long queries with the PostgreSQL formatter.

CommandWhat it does
Insert and get the new id back
Insert several rows
Skip rows that would break a unique key
Upsert; EXCLUDED is the row you tried to insert
Update with a join
Delete with a join
Delete and list what went
Latest order per user
Case-insensitive LIKE
Match any value in an array
Page 3 of 20 rows
Conditional count in one pass
Rank rows within each group
... Run several statements as one transaction
Undo everything since BEGIN
... Undo part of a transaction

Example: upsert a user, then report what each user has spent with a CTE.

sql
INSERT INTO users (email, name, plan) VALUES ('[email protected]', 'Ada King', 'team')
ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name, plan = EXCLUDED.plan
RETURNING id, plan;

WITH paid AS (
  SELECT user_id, sum(total) AS spent FROM orders WHERE status = 'paid' GROUP BY user_id
)
SELECT u.email, coalesce(p.spent, 0) AS spent
FROM users u LEFT JOIN paid p ON p.user_id = u.id
ORDER BY spent DESC;

Import and Export

reads and writes files on your machine. Plain uses files on the database server and needs superuser or the pg_write_server_files role.

CommandWhat it does
Export a table to CSV
Export a query to CSV
Import CSV into chosen columns
CSV from the shell, no needed
Server COPY that works for any role
Send query output to a file until the next
Run a SQL file inside the session
Run a file in one transaction, stop at the first error
Plain SQL dump of one database
Compressed custom format for pg_restore
Directory format, 4 tables in parallel
Schema only, no rows
Data only, one table
Everything except one table
Dump that restores under any role
Roles and tablespaces, which pg_dump skips
List what a dump contains
Restore into an existing empty database
Create the database named in the dump, then restore
DestructiveDrop each object before recreating it
Parallel restore of a directory dump
Turn a custom dump into a plain SQL file

In 1bench: the PostgreSQL GUI exports a whole database, one schema or one table to a .sql file with structure, data and sequence values, read inside one read-only transaction. It does not need pg_dump installed.

Example: back up the shop database every night and restore it into a fresh database to prove the backup works.

bash
pg_dump -Fc -d shop -f "shop-$(date +%F).dump"
createdb -O app shop_restore_check
pg_restore -d shop_restore_check --no-owner "shop-$(date +%F).dump"
psql -d shop_restore_check -c 'SELECT count(*) FROM orders'

Indexes and EXPLAIN

CommandWhat it does
B-tree index; blocks writes while it builds
Build without blocking writes
Unique, case-insensitive email
Partial index, only matching rows
Covering index for index-only scans
Index JSONB for and
Drop without blocking reads and writes
Rebuild a bloated index online
Indexes on a table with their sizes
Index definitions as SQL
Indexes never used since stats reset
Show the plan without running the query
Run the query and show real rows and times
Add cache hits, reads and output columns
Plan as JSON for visualizer tools
Refresh planner statistics after a bulk load

Since PostgreSQL 18, prints buffer counts without asking.

Example: check that the latest-orders query uses an index, and time a delete without keeping it.

sql
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 2 ORDER BY created_at DESC LIMIT 20;
-- Index Scan using orders_user_created_idx on orders ... (actual time=0.009..0.010 rows=20.00 loops=1)

BEGIN;
EXPLAIN ANALYZE DELETE FROM orders WHERE status = 'refunded';   -- really deletes
ROLLBACK;                                                       -- so put it back

Sessions and Locks

CommandWhat it does
Who is active and what they run
Longest-running queries first
Sessions holding an open transaction
Waiting sessions and the PIDs blocking them
Locks held or wanted on one table
Cancel the running query, keep the session
DestructiveKill the session, rolling back its transaction
DestructiveKill all connections to a database
Cancel this session's statements after 30 seconds
Give up waiting for a lock after 5 seconds
End forgotten open transactions for a role
Your own PID, to exclude from kill queries

Ctrl+C in psql cancels the running query, the same as .

In 1bench: the PostgreSQL GUI server overview shows connections against max_connections split into active and idle, the cache hit rate, uptime, and the largest databases with their size and connection count.

Example: find the session blocking an update and cancel its query, killing the session only if that fails.

sql
SELECT pid, pg_blocking_pids(pid) AS blocked_by, left(query, 60) AS query
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;   -- 26077 | {26074} | UPDATE users ...

SELECT pg_cancel_backend(26074);
SELECT pg_terminate_backend(26074);              -- if it is idle in transaction

Server Admin

CommandWhat it does
Full server version string
Just the version number
Path to postgresql.conf
Path to pg_hba.conf, the login rules
Where the data files live
Connection limit
Write a setting to postgresql.auto.conf
Apply config changes that need no restart
Changes waiting for a restart
Table size including indexes and TOAST
10 largest tables
Dead rows and last autovacuum per table
Reclaim dead rows and refresh statistics
DestructiveRewrites the table, blocks all access
Install an extension in this database
Extensions the server can install
Top queries by total time
true on a read replica
Is the server running, and its PID
Reload config from the shell
Restart, needed for settings like shared_buffers

pg_stat_statements also needs in postgresql.conf and a restart.

Example: raise work_mem for the whole server without a restart, then confirm it from a new session.

bash
psql -U postgres -c "ALTER SYSTEM SET work_mem = '64MB'" -c "SELECT pg_reload_conf()"
psql -U postgres -c 'SHOW work_mem'   # 64MB

Dates, JSONB and Strings

The common ones. A full date functions sheet and a JSONB sheet are coming. Escape quotes in values with the PostgreSQL escape tool.

CommandWhat it does
Current time, today, this time yesterday
First instant of the month, for grouping
Year as a number; also month, day, dow
Format a timestamp as text
Convert timestamptz to UTC wall time
One row per day, for gap-free reports
JSONB field as text
Nested JSONB field as text
Rows whose JSONB contains this object
Replace one key, value must be JSON
One row per array element
Join values from many rows into one string
Part of a string by delimiter
Escape a single quote by doubling it

Example: daily signup counts for the last week from the events table, with plan pulled from JSONB.

sql
SELECT d::date AS day, e.payload->>'plan' AS plan, count(e.id) AS signups
FROM generate_series(current_date - 6, current_date, interval '1 day') d
LEFT JOIN events e ON e.created_at::date = d::date AND e.type = 'signup'
GROUP BY 1, 2
ORDER BY 1;

Neon, Supabase, RDS and YugabyteDB

All four speak the Postgres protocol, so psql, pg_dump and every command above work. These rows differ.

CommandWhat it does
Neon; add to the endpoint ID for the pooled host
Print a Neon branch's connection string
Branch a Neon database, data included
Supabase session pooler, works over IPv4
Supabase direct connection, IPv6 unless you buy the IPv4 add-on
AWS CA bundle for verify-full on RDS and Aurora
Let an RDS or Aurora user log in with IAM
IAM token to use as the password, valid 15 minutes (PAM authentication failed?)
Aurora reader endpoint; drop for the writer
YugabyteDB shell, a psql fork with the same meta commands

RDS and Aurora have no superuser. The master user gets rds_superuser, and server settings live in parameter groups, so is refused.

In 1bench: the Amazon RDS GUI signs a fresh IAM token for every new connection and ships the RDS CA bundle, so verify-full works without downloading global-bundle.pem or exporting a new PGPASSWORD every 15 minutes.

Example: connect to RDS PostgreSQL with IAM auth and a verified TLS connection.

bash
curl -sO https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
export PGPASSWORD="$(aws rds generate-db-auth-token \
  --hostname shop.abc123.us-east-1.rds.amazonaws.com --port 5432 \
  --region us-east-1 --username app)"
psql "host=shop.abc123.us-east-1.rds.amazonaws.com dbname=shop user=app sslmode=verify-full sslrootcert=global-bundle.pem"

Gotchas

  • Unquoted names fold to lowercase. A table created as must be quoted in every query, and looks for .
  • does not grant table access. It covers CONNECT, CREATE and TEMP only, so users still get "permission denied for table" (error 42501) until you grant on the schema and tables.
  • only covers tables created later by the role it names (or by you, without ). Run it as, or for, the role that runs your migrations.
  • fails with "cannot alter type of a column used by a view or rule". Drop and recreate the view in the same transaction.
  • executes the statement. On an UPDATE or DELETE, wrap it in and .
  • On macOS, and can fail with "a worker process died unexpectedly" when the host is a name like localhost. It showed up in testing with the EDB 18.6 binaries. Setting fixed it, and so did connecting to 127.0.0.1.

Work with PostgreSQL without memorizing it

1bench is a desktop GUI for PostgreSQL. Connect, browse your data and run queries in one window, with the CLI still there when you want it.

Open PostgreSQL in 1bench

PostgreSQL FAQ

What port does PostgreSQL use?
5432 by default. Run SHOW port; in psql to see what your server listens on. Hosted services change it in a few places: the Supabase transaction pooler listens on 6543, and YugabyteDB's YSQL API uses 5433. If psql cannot connect, check the port in the URL first, then listen_addresses in postgresql.conf, which is localhost only on a fresh install.
Is PostgreSQL case sensitive?
Values yes, names no. 'Ada' = 'ada' is false, so use ILIKE or compare lower(email) for case-insensitive matches. Table and column names are folded to lowercase unless you double-quote them: CREATE TABLE Users makes users, but CREATE TABLE "Users" makes a table you must quote every time you query it. Most ORMs that quote names cause this.
Does Postgres need COMMIT?
Not by default. psql runs in autocommit mode, so each statement commits on its own. Once you type BEGIN, nothing is saved until COMMIT, and ROLLBACK throws the work away. A session left idle in transaction holds its locks and blocks vacuum, so set idle_in_transaction_session_timeout on app roles. \set AUTOCOMMIT off makes psql open a transaction for you before every statement.
Where is the postgresql.conf file located?
Run SHOW config_file; as a superuser. It prints the exact path the server loaded. SHOW hba_file; gives pg_hba.conf, which controls who may log in, and SHOW data_directory; gives the data folder. Settings changed with ALTER SYSTEM go to postgresql.auto.conf in the data directory and override postgresql.conf.
How do I find which session is locking a table?
Query pg_stat_activity with pg_blocking_pids(pid). Every waiting session lists the PIDs blocking it, and the blocker is often a session idle in transaction rather than a running query. Cancel the blocker's statement with pg_cancel_backend(pid), or end the session with pg_terminate_backend(pid), which rolls back its open transaction.