psql Basics
Connecting to Postgres
psql is the interactive terminal that ships with every Postgres installation. It's the fastest way to run queries, explore your schema, and manage your database — and once you learn a few tricks, it becomes an incredibly productive environment.
There are two main ways to connect. The first is a connection URI, which packs everything into a single string:
psql postgresql://myuser:mypassword@localhost:5432/mydb
The second approach uses individual flags, which I find easier to read when I'm working with multiple servers:
psql -h localhost -p 5432 -U myuser -d mydb
Here's what each flag means:
| Flag | Purpose |
|---|---|
-h | Hostname or IP address |
-p | Port (default is 5432) |
-U | Username |
-d | Database name |
Once connected, you'll see a prompt like this:
mydb=>
The => means you're connected as a regular user. If you see =#, you're connected as a superuser. You can check your current connection details at any time:
mydb=> \conninfo You are connected to database "mydb" as user "myuser" via socket in "/tmp" at port "5432".
aside positive Snowflake Postgres supports standard psql connections — use your existing psql client with
psql postgresql://user:pass@your-instance.snowflakecomputing.com:5432/dbname. All the meta-commands and settings in this guide work with Snowflake Postgres.
Meta-Commands and Query Execution
Meta-commands are the backslash commands that make psql so much more than a SQL prompt. They let you explore your database without writing information_schema queries by hand.
Exploring Your Database
Here are the commands I use every single day:
| Command | What it shows |
|---|---|
\l | List all databases |
\c dbname | Connect to a different database |
\dt | List tables in current schema |
\dt+ | List tables with sizes |
\d tablename | Describe a table (columns, types, constraints) |
\dn | List schemas |
\du | List roles and their attributes |
\di | List indexes |
\df | List functions |
Let's look at a few of these in action:
mydb=> \dt List of relations Schema | Name | Type | Owner --------+---------------+-------+--------- public | customers | table | myuser public | orders | table | myuser public | products | table | myuser (3 rows)
The \d command is probably the one I use most — it tells you everything about a table's structure:
mydb=> \d customers Table "public.customers" Column | Type | Collation | Nullable | Default ------------+------------------------+-----------+----------+--------------------------------------- id | integer | | not null | nextval('customers_id_seq'::regclass) name | character varying(100) | | not null | email | character varying(255) | | | created_at | timestamp with time zone | | not null | now() Indexes: "customers_pkey" PRIMARY KEY, btree (id) "customers_email_key" UNIQUE CONSTRAINT, btree (email)
Running Queries
Type any SQL statement followed by a semicolon:
mydb=> SELECT name, email FROM customers LIMIT 3; name | email -------------+--------------------- Alice Chen | alice@example.com Bob Smith | bob@example.com Carol Jones | carol@example.com (3 rows)
For multiline queries, psql waits until it sees a semicolon — the prompt changes to show you're continuing a statement:
mydb=> SELECT name, mydb-> count(*) AS order_count mydb-> FROM customers c mydb-> JOIN orders o ON c.id = o.customer_id mydb-> GROUP BY name mydb-> ORDER BY order_count DESC;
Expanded Display
Wide tables are hard to read in the terminal. Toggle expanded mode with \x:
mydb=> \x Expanded display is on. mydb=> SELECT * FROM customers WHERE id = 1; -[ RECORD 1 ]---------------------------- id | 1 name | Alice Chen email | alice@example.com created_at | 2024-11-15 09:23:14.521+00
I recommend \x auto — it uses expanded mode only when the output is too wide for your terminal.
Timing and Performance
Turn on query timing to see how long each statement takes:
mydb=> \timing Timing is on. mydb=> SELECT count(*) FROM orders; count -------- 284710 (1 row) Time: 42.138 ms
This is invaluable when you're testing index changes or comparing query plans.
Getting Help
Two help commands worth knowing: \h shows SQL syntax help, and \? lists all meta-commands:
mydb=> \h CREATE INDEX Command: CREATE INDEX Description: define a new index Syntax: CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON ...
Customizing Your psql Environment
This is where psql goes from good to great. I wanted to share a couple of my favorite things you can do with your environment — the settings that make psql feel like home.
Making NULLs Visible
By default, NULL values display as blank — indistinguishable from an empty string. This has tripped up every Postgres user at some point. Fix it:
mydb=> \pset null '(null)' Null display is "(null)". mydb=> SELECT name, phone FROM customers WHERE id IN (1, 2); name | phone -------------+------------- Alice Chen | 555-0142 Bob Smith | (null) (2 rows)
Pretty Output with Unicode Borders
mydb=> \pset linestyle unicode Line style is unicode. mydb=> \pset border 2 Border style is 2. mydb=> SELECT name, email FROM customers LIMIT 2; ┌─────────────┬─────────────────────┐ │ name │ email │ ├─────────────┼─────────────────────┤ │ Alice Chen │ alice@example.com │ │ Bob Smith │ bob@example.com │ └─────────────┴─────────────────────┘
The .psqlrc File
Every setting you've been typing manually can go in ~/.psqlrc so it loads automatically. Here's a starter file that I recommend:
-- Make NULLs visible \pset null '(null)' -- Unicode borders \pset linestyle unicode \pset border 2 -- Show timing on every query \timing -- Use expanded mode when needed \x auto -- Quiet startup messages \set QUIET 1 -- Custom prompt showing database and user \set PROMPT1 '%n@%/%R%# ' -- Don't page short results \pset pager off \set QUIET 0
Save this to ~/.psqlrc and every new psql session starts with your preferred settings.
Seeing the SQL Behind Meta-Commands
One of my favorite tricks: \set ECHO_HIDDEN on shows you the actual SQL query that psql runs when you type a meta-command. This is a great way to learn:
mydb=> \set ECHO_HIDDEN on mydb=> \dt ********* QUERY ********** SELECT n.nspname as "Schema", c.relname as "Name", CASE c.relkind WHEN 'r' THEN 'table' ... FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','p','') ... **************************
Saved Queries
You can save frequently-used queries as psql variables and run them with a colon:
-- Add to .psqlrc \set active_queries 'SELECT pid, state, query FROM pg_stat_activity WHERE state != \'idle\';'
Then use it:
mydb=> :active_queries pid | state | query ------+--------+------------------------------------- 1234 | active | SELECT * FROM orders WHERE ... (1 row)
History Search and Tab Completion
Two features that save me time every day:
- Ctrl+R — Search your command history. Start typing any part of a previous command and it finds matches.
- Tab completion — psql completes table names, column names, SQL keywords, and even meta-commands. Start typing and hit Tab.
Try it: type SELECT * FROM cust and press Tab — psql will complete it to customers.
Importing and Exporting Data
psql has built-in commands for moving data in and out of your database without external tools.
Running SQL Files
The \i command executes a SQL file:
mydb=> \i /path/to/schema.sql CREATE TABLE CREATE TABLE CREATE INDEX
This is how I run migration scripts and seed data files during development.
Sending Output to a File
Use \o to redirect query output:
mydb=> \o /tmp/report.txt mydb=> SELECT * FROM orders WHERE total > 1000; mydb=> \o
The first \o starts capturing, and \o with no argument stops it.
Using an External Editor
Type \e to open your current query (or an empty buffer) in your $EDITOR. Write your SQL, save, and quit — psql runs it immediately. This is especially nice for complex queries where you want syntax highlighting and proper indentation.
COPY for Bulk Data
\copy is psql's client-side bulk import/export command:
-- Export to CSV mydb=> \copy orders TO '/tmp/orders.csv' WITH CSV HEADER; COPY 284710 -- Import from CSV mydb=> \copy staging_orders FROM '/tmp/new_orders.csv' WITH CSV HEADER; COPY 500
The \copy command runs on your local machine — it reads and writes files from your filesystem, not the server's. This is important when you're connected to a remote database.
Conclusion
psql is a deceptively powerful tool. The basics get you running queries in seconds, but the meta-commands, customization options, and .psqlrc file turn it into a full development environment. I encourage you to experiment with the settings here, build up your own .psqlrc, and explore — there's always another trick to discover.
Related Resources
Interactive playground to practice psql commands.
Complete reference for psql commands, variables, and shortcuts.
Connecting to Snowflake Postgres and essential commands.
Settings, presets, echo, and saved queries for power users.
This content is provided as is, and is not maintained on an ongoing basis. It may be out of date with current Snowflake instances