Skip to content

/cheatsheets/postgresql/psql-cheat-sheet

psql cheat sheet: connect, list, describe and run SQL files

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated 2026-10-20
tool
PostgreSQL
versions
psql 14+
os
any

The psql commands used every day. Backslash commands are interpreted by psql itself, not sent to the server, and do not end with a semicolon; SQL statements do.

Connect

bash
psql -h localhost -p 5432 -U app -d appdb
psql "postgresql://app@localhost:5432/appdb?sslmode=require"
psql -d appdb -c "select now()"                 # run one command and exit
psql -d appdb -f migrate.sql -v ON_ERROR_STOP=1 # run a file, stop at the first error

The password comes from a prompt, PGPASSWORD, or ~/.pgpass.

Meta-commands

CommandDoes
\llist databases
\c dbnameconnect to another database
\dnlist schemas
\dt [schema.*]list tables
\d tablecolumns, indexes and constraints of a table
\d+ tablethe same, with size and storage details
\di, \dv, \dfindexes, views, functions
\duroles
\x [auto]expanded (vertical) output for wide rows
\timingshow how long each statement takes
\eedit the last query in $EDITOR
\i file.sqlrun a file
\copy (query) to file.csv csv headerexport to a CSV on the client machine
\conninfocurrent connection
\qquit

The same information with SQL

Useful from any client or driver. Run here against PostgreSQL 18 with PGlite:

bash
npm init -y >/dev/null
npm install --silent --no-audit --no-fund @electric-sql/pglite@0.5.10 >/dev/null 2>&1
cat > q.mjs <<'EOF'
import { PGlite } from "@electric-sql/pglite";
const db = new PGlite();
await db.exec(`create table users (id int primary key, email text not null unique);
               create index users_email_lower on users (lower(email));`);
const q = async (sql) => console.table((await db.query(sql)).rows);
await q(`select current_database() as db, current_user as "user", current_setting('server_version') as version`);
await q(`select table_schema, table_name from information_schema.tables where table_schema = 'public'`);
await q(`select column_name, data_type, is_nullable from information_schema.columns where table_name = 'users' order by ordinal_position`);
await q(`select indexname, indexdef from pg_indexes where tablename = 'users' order by indexname`);
EOF
node q.mjs

Output

┌─────────┬────────────┬────────────┬─────────┐
│ (index) │ db         │ user       │ version │
├─────────┼────────────┼────────────┼─────────┤
│ 0       │ 'postgres' │ 'postgres' │ '18.6'  │
└─────────┴────────────┴────────────┴─────────┘
┌─────────┬──────────────┬────────────┐
│ (index) │ table_schema │ table_name │
├─────────┼──────────────┼────────────┤
│ 0       │ 'public'     │ 'users'    │
└─────────┴──────────────┴────────────┘
┌─────────┬─────────────┬───────────┬─────────────┐
│ (index) │ column_name │ data_type │ is_nullable │
├─────────┼─────────────┼───────────┼─────────────┤
│ 0       │ 'id'        │ 'integer' │ 'NO'        │
│ 1       │ 'email'     │ 'text'    │ 'NO'        │
└─────────┴─────────────┴───────────┴─────────────┘
┌─────────┬─────────────────────┬─────────────────────────────────────────────────────────────────────────────┐
│ (index) │ indexname           │ indexdef                                                                    │
├─────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ 0       │ 'users_email_key'   │ 'CREATE UNIQUE INDEX users_email_key ON public.users USING btree (email)'   │
│ 1       │ 'users_email_lower' │ 'CREATE INDEX users_email_lower ON public.users USING btree (lower(email))' │
│ 2       │ 'users_pkey'        │ 'CREATE UNIQUE INDEX users_pkey ON public.users USING btree (id)'           │
└─────────┴─────────────────────┴─────────────────────────────────────────────────────────────────────────────┘

Sources