PostgreSQL Reference
0. Connection & Basics
0.1 Client Connect
Connecting to a database via the psql
CLI.
# Connect to 'mydb' as 'user' on 'localhost'
psql -h localhost -U user -d mydb
# Execute a single command and exit
psql -c "SELECT version();"PostgreSQL 16.1 on x86_64-pc-linux-gnu...
0.2 CLI Meta-commands
Internal psql commands for database
introspection.
\l -- List all databases
\c mydb -- Connect to 'mydb'
\dt -- List all tables in current schema
\d+ users -- Describe table 'users' in detail
\i file.sql -- Execute commands from file.sql
\q -- Quit psql(Introspection results)
0.3 Comments
Annotating SQL code.
-- Single-line comment
/* Multi-line
comment block */
SELECT 1; -- Trailing comment ?column?
----------
1
1. Data Types
1.1 Numerics
Standard and auto-incrementing types.
SELECT
42::SMALLINT, -- 2-byte int
100000::INTEGER, -- 4-byte int
10000000000::BIGINT, -- 8-byte int
3.1415::NUMERIC(6,4),-- Exact decimal
'NaN'::REAL; -- 4-byte float int2 | int4 | int8 | numeric | real
------+--------+-------------+---------+------
42 | 100000 | 10000000000 | 3.1415 | NaN
1.2 Strings & Binaries
Text and raw byte storage.
SELECT
'fixed'::CHAR(5), -- Padded string
'var'::VARCHAR(10), -- Limited string
'unlimited'::TEXT, -- Preferred for most text
'\xDEADBEEF'::BYTEA; -- Variable-length binary char | varchar | text | bytea
-------+---------+-----------+------------
fixed | var | unlimited | \xdeadbeef
1.3 Date & Time
Temporal types with timezone support.
SELECT
CURRENT_DATE, -- Date only
CURRENT_TIME, -- Time with TZ
NOW() AT TIME ZONE 'UTC', -- Timestamp without TZ
INTERVAL '1 day 2 hours' AS duration; -- Time span date | time | timezone | duration
------------+------------------+----------------------------+----------------
2024-05-16 | 10:00:00.123+00 | 2024-05-16 10:00:00.123456 | 1 day 02:00:00
1.4 Booleans & Enums
Logical values and custom sets.
CREATE TYPE user_status AS ENUM ('active', 'banned');
SELECT
true::BOOLEAN AS is_ok,
'active'::user_status; is_ok | user_status
-------+-------------
t | active
2. Basic Querying (DQL)
2.1 SELECT & Aliases
Retrieving and naming columns.
SELECT
first_name || ' ' || last_name AS full_name,
UPPER(email) AS contact_email
FROM users; full_name | contact_email
---------------+-------------------
Alice Johnson | ALICE@EXAMPLE.COM
2.2 WHERE Filtering
Conditional logic and pattern matching.
SELECT * FROM products
WHERE price > 100
AND category IN ('electronics', 'tools')
AND name ~* '^pro.*'; -- Case-insensitive POSIX regex(Filtered rows)
2.3 ORDER BY
Sorting results.
SELECT name, price FROM products
ORDER BY category ASC, price DESC NULLS LAST;(Sorted rows)
2.4 LIMIT / OFFSET
Pagination.
SELECT id FROM events
ORDER BY created_at DESC
LIMIT 10 OFFSET 20; -- Skip first 20, take next 10(10 rows)
3. Joins & Set Operations
3.1 Inner & Outer Joins
Combining tables based on keys.
SELECT u.name, o.id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL; -- Users with no orders(Users without orders)
3.2 Cross & Self Joins
Cartesian products and self-references.
-- Self join for hierarchical data
SELECT e.name AS emp, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;(Pairs of employees and managers)
3.3 UNION, INTERSECT, EXCEPT
Combining result sets.
SELECT email FROM leads
UNION -- Removes duplicates (UNION ALL keeps them)
SELECT email FROM customers
EXCEPT
SELECT email FROM unsubscribed;(Distinct active emails)
3.4 JOIN with Subqueries
Using derived tables.
SELECT u.name, recent.max_date
FROM users u
JOIN (
SELECT user_id, MAX(created_at) AS max_date
FROM logins GROUP BY user_id
) AS recent ON u.id = recent.user_id;(Users with their last login date)
4. Aggregation & Grouping
4.1 GROUP BY
Summarizing data by category.
SELECT category, COUNT(*), AVG(price)
FROM products
GROUP BY category; category | count | avg
----------+-------+-------------------
tools | 15 | 42.50000000000000
4.2 HAVING
Filtering groups.
SELECT user_id, SUM(total)
FROM orders
GROUP BY user_id
HAVING SUM(total) > 1000;(High-value customers)
4.3 Aggregate Functions
Postgres-specific aggregates.
SELECT
COUNT(DISTINCT category) AS cats,
STRING_AGG(name, ', ' ORDER BY name) AS all_names,
ARRAY_AGG(id) AS id_list
FROM products; cats | all_names | id_list
------+------------------+-----------
5 | Drill, Saw, Wren | {10,12,5}
5. Advanced Querying
5.1 Subqueries (IN, EXISTS)
Correlated and non-correlated subqueries.
SELECT name FROM employees e
WHERE EXISTS (
SELECT 1 FROM salaries s
WHERE s.emp_id = e.id AND s.amount > 100000
);(High earners)
5.2 Common Table Expressions (WITH)
Improving readability and reusability.
WITH regional_sales AS (
SELECT region, SUM(amount) AS total FROM sales GROUP BY region
)
SELECT region FROM regional_sales
WHERE total > (SELECT AVG(total) FROM regional_sales);(Above-average regions)
5.3 Recursive CTEs
Iterating over hierarchical or graph data.
WITH RECURSIVE subordinates AS (
SELECT id, name, manager_id FROM employees WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id
FROM employees e
JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;(The entire management chain starting at ID 1)
6. Window Functions
6.1 OVER & PARTITION BY
Calculations across rows related to the current row.
SELECT
name,
price,
AVG(price) OVER(PARTITION BY category) AS cat_avg
FROM products;(Product names, prices, and their category average)
6.2 Ranking
Assigning sequence numbers.
SELECT
name,
ROW_NUMBER() OVER(ORDER BY score DESC) AS pos,
RANK() OVER(ORDER BY score DESC) AS rnk
FROM players;(Players ranked by score)
6.3 Analytics
Accessing other rows without joins.
SELECT
day,
value,
LAG(value) OVER(ORDER BY day) AS prev_val,
value - LAG(value) OVER(ORDER BY day) AS delta
FROM daily_stats;(Daily values and their change from previous day)
7. Data Modification (DML)
7.1 INSERT
Adding rows with RETURNING.
INSERT INTO users (name, email)
VALUES ('Bob', 'bob@example.com')
RETURNING id, created_at; -- Get generated values back immediately id | created_at
----+-------------------------------
42 | 2024-05-16 10:00:00.123456+00
7.2 UPDATE & DELETE
Modifying existing data.
UPDATE products SET price = price * 1.1
WHERE category = 'luxury'
RETURNING name, price AS new_price;
DELETE FROM sessions WHERE last_seen < NOW() - INTERVAL '1 hour';(Affected rows)
7.3 Upsert / Merge
Handling conflicts during insert.
INSERT INTO user_prefs (user_id, theme)
VALUES (1, 'dark')
ON CONFLICT (user_id)
DO UPDATE SET theme = EXCLUDED.theme, updated_at = NOW();INSERT 0 1
8. Schema Definition (DDL)
8.1 CREATE & ALTER Table
Defining and evolving structure.
CREATE TABLE items (
id SERIAL PRIMARY KEY, -- Auto-incrementing 4-byte int
uuid UUID DEFAULT gen_random_uuid(),
metadata JSONB NOT NULL DEFAULT '{}'
);
ALTER TABLE items ADD COLUMN tags TEXT[] DEFAULT '{}';CREATE TABLE
8.2 Constraints
Ensuring data integrity.
CREATE TABLE accounts (
id INT PRIMARY KEY,
email TEXT UNIQUE NOT NULL CHECK (email ~* '@'),
balance NUMERIC DEFAULT 0 CHECK (balance >= 0),
parent_id INT REFERENCES accounts(id) ON DELETE CASCADE
);CREATE TABLE
8.3 Indexes & Performance
Optimizing queries.
CREATE INDEX idx_user_email ON users (email);
CREATE INDEX idx_item_meta ON items USING GIN (metadata); -- GIN for JSONBCREATE INDEX
8.4 Views & Materialized Views
Stored queries and cached results.
CREATE VIEW active_users AS SELECT * FROM users WHERE status = 'active';
-- Materialized view requires manual refresh
CREATE MATERIALIZED VIEW sales_summary AS
SELECT region, SUM(amount) FROM sales GROUP BY region;
REFRESH MATERIALIZED VIEW sales_summary;REFRESH MATERIALIZED VIEW
9. Logic & Administration
9.1 Transactions (ACID)
Ensuring atomic operations.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- Or ROLLBACK on errorCOMMIT
9.2 Triggers & Functions
Server-side logic.
CREATE FUNCTION update_timestamp() RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_update_items
BEFORE UPDATE ON items
FOR EACH ROW EXECUTE FUNCTION update_timestamp();CREATE TRIGGER
9.3 Explain Plan & Optimization
Analyzing query execution.
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 500; Index Scan using idx_orders_user_id on orders...
Execution Time: 0.042 ms
9.4 Roles, Permissions & Schemas
Security and organization.
CREATE ROLE read_only;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only;
CREATE SCHEMA archive;
ALTER TABLE old_logs SET SCHEMA archive;ALTER TABLE