Getting Started
PostgreSQL (also known as Postgres) is a powerful, open source object-relational database system with over 35 years of active development. It has earned a strong reputation for reliability, feature robustness, and performance. PostgreSQL runs on all major operating systems and has been ACID-compliant since 2001.
ACID Compliance
Full atomicity, consistency, isolation, and durability for all transactions.
MVCC
Multi-Version Concurrency Control enables high concurrency without locking.
Extensibility
Create custom data types, functions, operators, and index methods.
SQL Standard
Highly conformant to the SQL standard with powerful extensions.
Installation
# macOS (Homebrew) brew install postgresql@18 brew services start postgresql@18 # Ubuntu / Debian sudo apt install -y postgresql postgresql-contrib sudo systemctl start postgresql # Windows โ download the installer from: # https://www.postgresql.org/download/windows/ # Verify installation psql --version # PostgreSQL 18.x
Create Your First Database
-- Connect as superuser psql -U postgres -- Create a database CREATE DATABASE myapp_db; -- Create a user with password CREATE USER myapp_user WITH ENCRYPTED PASSWORD 'securepassword'; -- Grant privileges GRANT ALL PRIVILEGES ON DATABASE myapp_db TO myapp_user; -- Connect to the database \c myapp_db
\l to list databases, \dt to list tables, and
\d tablename to describe a table in the psql shell.Data Types
PostgreSQL supports a rich set of native data types. Choosing the right type improves performance and data integrity.
| Type | SQL Name | Storage | Description |
|---|---|---|---|
integer |
INT4 |
4 bytes | Signed 4-byte integer (-2B to 2B) |
bigint |
INT8 |
8 bytes | Signed 8-byte integer (ยฑ9.2 quintillion) |
numeric |
DECIMAL |
variable | Exact, user-specified precision โ ideal for money |
double precision |
FLOAT8 |
8 bytes | IEEE 754 double-precision floating point |
serial |
SERIAL4 |
4 bytes | Auto-incrementing integer (use GENERATED IDENTITY in PG10+) |
text |
TEXT |
variable | Variable-length character string, unlimited |
varchar(n) |
CHARACTER VARYING |
variable | Variable-length string with limit |
boolean |
BOOL |
1 byte | Logical boolean (TRUE / FALSE) |
date |
DATE |
4 bytes | Calendar date (year, month, day) |
timestamp |
TIMESTAMP |
8 bytes | Date and time without timezone |
timestamptz |
TIMESTAMPTZ |
8 bytes | Date and time with timezone โ recommended |
uuid |
UUID |
16 bytes | Universally unique identifier |
jsonb |
JSONB |
variable | Binary JSON โ faster querying, supports indexing |
array |
TYPE[] |
variable | Array of any base type |
Array Example
CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, tags TEXT[], scores INTEGER[] ); INSERT INTO products (name, tags, scores) VALUES ('Widget A', ARRAY['electronics', 'gadget'], ARRAY[90, 85, 92]); -- Query with array operators SELECT * FROM products WHERE 'electronics' = ANY(tags); -- Unnest array into rows SELECT name, unnest(tags) AS tag FROM products;
PostgreSQL 18 โ New Features
PostgreSQL 18 ships with major improvements: uuidv7() for timestamp-ordered UUIDs, virtual generated columns by default, OLD/NEW in RETURNING clauses, skip-scan B-tree indexes, OAuth 2.0 authentication, data checksums enabled by default, and pg_upgrade retaining optimizer statistics.
uuidv7()
Time-ordered UUIDs with embedded millisecond timestamps. Far better index locality than random UUIDv4.
Virtual Generated Columns
Generated columns now default to VIRTUAL (computed on read, not stored), saving disk space automatically.
OLD/NEW in RETURNING
Access both old and new row values in INSERT, UPDATE, DELETE, and MERGE RETURNING clauses.
B-tree Skip Scan
Multicolumn indexes can now be used without a leading column condition โ more index reuse.
OAuth Authentication
Built-in OAuth 2.0 / OIDC support for modern identity provider integrations.
Checksums Default On
Data checksums now enabled by default on new clusters. pg_upgrade retains optimizer statistics.
uuidv7() โ Time-Ordered UUIDs
-- UUIDv7 embeds millisecond timestamp in high bits (PG 18+) CREATE TABLE events ( id UUID PRIMARY KEY DEFAULT uuidv7(), event_type TEXT NOT NULL, payload JSONB ); -- UUIDs sort chronologically โ excellent B-tree locality SELECT uuidv7(); SELECT uuid_extract_timestamp(uuidv7()); -- UUIDv4 comparison (random, bad B-tree locality) SELECT gen_random_uuid();
Generated Columns (Virtual Default)
-- VIRTUAL (PG18 default): computed on read, no extra storage CREATE TABLE products ( price NUMERIC(10,2), tax_rate NUMERIC(4,4) DEFAULT 0.18, price_incl NUMERIC(10,2) GENERATED ALWAYS AS (price * (1 + tax_rate)) VIRTUAL ); -- STORED: persisted to disk, can be directly indexed CREATE TABLE contacts ( first_name TEXT, last_name TEXT, full_name TEXT GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED ); CREATE INDEX ON contacts(full_name);
OLD / NEW in RETURNING
UPDATE products SET price = price * 1.10 WHERE category = 'electronics' RETURNING OLD.price AS old_price, NEW.price AS new_price, NEW.id; DELETE FROM orders WHERE status = 'cancelled' RETURNING OLD.*; MERGE INTO inventory i USING shipment s ON i.sku = s.sku WHEN MATCHED THEN UPDATE SET qty = i.qty + s.qty WHEN NOT MATCHED THEN INSERT (sku, qty) VALUES (s.sku, s.qty) RETURNING merge_action(), NEW.sku, NEW.qty;
B-tree Skip Scan
CREATE INDEX idx_dept_salary ON employees(department, salary); -- PG 18 skip scan: uses index even WITHOUT condition on 'department' SELECT * FROM employees WHERE salary > 100000; SET enable_skipscan = on; -- default in PG 18 SET enable_skipscan = off; -- disable for testing
Queries
SELECT Fundamentals
SELECT first_name, last_name, email, EXTRACT(YEAR FROM created_at) AS signup_year FROM users WHERE active = TRUE AND created_at >= '2023-01-01' ORDER BY last_name ASC, first_name ASC LIMIT 20 OFFSET 0;
JOINs
-- INNER JOIN SELECT o.id, u.email, o.total FROM orders o INNER JOIN users u ON o.user_id = u.id; -- LEFT JOIN SELECT u.email, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.email; -- FULL OUTER JOIN with COALESCE SELECT COALESCE(a.name, b.name) AS name, a.value AS left_val, b.value AS right_val FROM table_a a FULL OUTER JOIN table_b b ON a.id = b.id;
Common Table Expressions (CTEs)
-- Recursive CTE WITH RECURSIVE seq(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM seq WHERE n < 10 ) SELECT * FROM seq; -- Multi-step CTE pipeline WITH active_users AS (SELECT id, email FROM users WHERE active = TRUE), top_spenders AS ( SELECT user_id, SUM(amount) AS total FROM payments GROUP BY user_id HAVING SUM(amount) > 1000) SELECT au.email, ts.total FROM active_users au JOIN top_spenders ts ON au.id = ts.user_id ORDER BY ts.total DESC;
Window Functions
SELECT name, department, salary, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank, SUM(salary) OVER (PARTITION BY department) AS dept_total, LAG(salary, 1) OVER (PARTITION BY department ORDER BY salary) AS prev_salary, PERCENT_RANK() OVER (ORDER BY salary) AS pct_rank FROM employees;
Built-in Functions
String Functions
| Function | Example | Result |
|---|---|---|
length(s) |
length('hello') |
5 |
upper(s) |
upper('hello') |
HELLO |
lower(s) |
lower('WORLD') |
world |
trim(s) |
trim(' hi ') |
hi |
substring(s, start, len) |
substring('abcdef', 2, 3) |
bcd |
concat(s1, s2, ...) |
concat('foo', 'bar') |
foobar |
replace(s, from, to) |
replace('abc','b','X') |
aXc |
split_part(s, delim, n) |
split_part('a,b,c',',',2) |
b |
regexp_match(s, pattern) |
regexp_match('foo123', '\d+') |
{123} |
format(fmt, ...) |
format('Hello %s!', 'World') |
Hello World! |
Math Functions
SELECT abs(-42), ceil(4.2), floor(4.9), round(4.567, 2), sqrt(144), power(2, 10), mod(17, 5), random(), trunc(4.789), log(100), ln(2.71828);
JSON & JSONB
PostgreSQL has first-class support for JSON data. JSONB stores data in a decomposed binary format that is slightly slower to input but significantly faster to query. JSONB also supports GIN indexing.
CREATE TABLE events ( id BIGSERIAL PRIMARY KEY, payload JSONB NOT NULL ); INSERT INTO events (payload) VALUES ('{"type":"click","user":{"id":42,"name":"Alice"},"ts":1700000000}'); -- Operators: -> returns JSON, ->> returns text SELECT payload ->> 'type' AS event_type, payload -> 'user' ->> 'name' AS user_name, (payload ->> 'ts')::BIGINT AS timestamp FROM events; -- Containment operator @> SELECT * FROM events WHERE payload @> '{"user":{"id":42}}'; -- GIN index for fast JSONB querying CREATE INDEX idx_events_payload ON events USING gin(payload);
-> and ->>
-> returns JSON child; ->> returns
text.
@> Containment
Tests whether left JSONB contains right JSONB. Works with GIN indexes.
jsonb_path_query
SQL/JSON path language: jsonb_path_query(doc, '$.users[*].name')
Indexes
Indexes are critical for query performance. PostgreSQL supports multiple index types, each optimized for different workloads.
B-Tree
Best for equality and range queries. Works on any sortable type.
GIN
Generalized Inverted Index. Ideal for arrays, JSONB, and full-text search.
GiST
Generalized Search Tree. Used for geometric data and nearest-neighbor searches.
BRIN
Block Range INdex. Very small footprint for naturally ordered large tables.
CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC); CREATE INDEX idx_active_users ON users(email) WHERE active = TRUE; CREATE UNIQUE INDEX idx_users_email_unique ON users(email); CREATE INDEX idx_lower_email ON users(lower(email)); CREATE INDEX idx_fts ON articles USING gin( to_tsvector('english', title || ' ' || body)); REINDEX INDEX CONCURRENTLY idx_users_email;
EXPLAIN ANALYZE to verify an index is actually being used. Too many indexes can
slow down INSERT/UPDATE/DELETE operations.Data Definition (DDL)
Tables โ CREATE, ALTER, DROP
CREATE TABLE orders ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, status TEXT NOT NULL DEFAULT 'pending', total NUMERIC(12,2) CHECK (total >= 0), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ALTER TABLE orders ADD COLUMN notes TEXT; ALTER TABLE orders RENAME COLUMN notes TO memo; ALTER TABLE orders DROP COLUMN memo; DROP TABLE customer_orders CASCADE; TRUNCATE TABLE orders RESTART IDENTITY CASCADE;
Row-Level Security (RLS)
ALTER TABLE documents ENABLE ROW LEVEL SECURITY; CREATE POLICY doc_owner ON documents USING (owner_id = current_user_id()); CREATE POLICY admin_all ON documents TO admin_role USING (TRUE);
Data Manipulation (DML)
INSERT / UPDATE / DELETE
INSERT INTO products (name, price) VALUES ('Widget A', 9.99), ('Widget B', 14.99); INSERT INTO users (email) VALUES ('alice@example.com') RETURNING id, created_at; UPDATE products SET price = price * 1.10 WHERE category = 'electronics'; DELETE FROM sessions WHERE expires_at < now();
UPSERT & MERGE
-- UPSERT INSERT INTO product_counts (product_id, count) VALUES (42, 1) ON CONFLICT (product_id) DO UPDATE SET count = product_counts.count + EXCLUDED.count; -- MERGE (PG 15+) MERGE INTO customer_accounts ca USING new_transactions nt ON ca.account_id = nt.account_id WHEN MATCHED THEN UPDATE SET balance = ca.balance + nt.amount WHEN NOT MATCHED THEN INSERT (account_id, balance) VALUES (nt.account_id, nt.amount); -- COPY bulk load COPY products (name, price) FROM '/tmp/products.csv' WITH (FORMAT csv, HEADER true);
PL/pgSQL โ Stored Functions & Procedures
PL/pgSQL is PostgreSQL's built-in procedural language. Functions return values
and can appear in queries. Procedures (called with CALL) can
manage their own transactions.
CREATE OR REPLACE FUNCTION calc_tax(price NUMERIC, rate NUMERIC DEFAULT 0.18) RETURNS NUMERIC AS $$ BEGIN RETURN price * (1 + rate); END; $$ LANGUAGE plpgsql IMMUTABLE; -- Set-returning function CREATE OR REPLACE FUNCTION active_orders(uid INT) RETURNS TABLE(order_id INT, total NUMERIC, status TEXT) AS $$ BEGIN RETURN QUERY SELECT id, total, status FROM orders WHERE user_id = uid AND status != 'cancelled'; END; $$ LANGUAGE plpgsql;
Stored Procedures
CREATE OR REPLACE PROCEDURE transfer_funds( from_id INT, to_id INT, amount NUMERIC ) LANGUAGE plpgsql AS $$ DECLARE bal NUMERIC; BEGIN SELECT balance INTO bal FROM accounts WHERE id = from_id FOR UPDATE; IF bal < amount THEN RAISE EXCEPTION 'Insufficient funds'; END IF; UPDATE accounts SET balance = balance - amount WHERE id = from_id; UPDATE accounts SET balance = balance + amount WHERE id = to_id; COMMIT; END; $$; CALL transfer_funds(101, 202, 500.00);
Triggers
Triggers automatically execute a function in response to INSERT, UPDATE, DELETE, or TRUNCATE. They fire BEFORE or AFTER the event, at either row or statement level.
-- Auto-set updated_at CREATE OR REPLACE FUNCTION set_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at := now(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_updated_at BEFORE INSERT OR UPDATE ON users FOR EACH ROW EXECUTE FUNCTION set_updated_at(); -- Audit log trigger CREATE OR REPLACE FUNCTION audit_changes() RETURNS TRIGGER AS $$ BEGIN INSERT INTO audit_log(table_name, operation, old_data, new_data) VALUES(TG_TABLE_NAME, TG_OP, CASE WHEN TG_OP != 'INSERT' THEN row_to_json(OLD) END, CASE WHEN TG_OP != 'DELETE' THEN row_to_json(NEW) END); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql; -- Conditional trigger CREATE TRIGGER trg_price_change AFTER UPDATE OF price ON products FOR EACH ROW WHEN (OLD.price IS DISTINCT FROM NEW.price) EXECUTE FUNCTION notify_price_change();
Concurrency Control
PostgreSQL uses MVCC โ each transaction sees a consistent snapshot. Reading never blocks writing and writing never blocks reading.
BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; -- Skip locked rows (queue pattern) SELECT * FROM job_queue WHERE status = 'pending' ORDER BY created_at LIMIT 1 FOR UPDATE SKIP LOCKED; -- Advisory locks SELECT pg_advisory_lock(12345); SELECT pg_try_advisory_lock(12345); SELECT pg_advisory_unlock(12345);
| Level | Dirty Read | Non-repeatable | Phantom | Notes |
|---|---|---|---|---|
READ COMMITTED |
โ | โ | โ | Default level |
REPEATABLE READ |
โ | โ | โ (PG) | Snapshot at txn start |
SERIALIZABLE |
โ | โ | โ | Uses SSI; may raise errors |
Table Partitioning
-- Range partitioning CREATE TABLE measurement ( city_id INT, logdate DATE NOT NULL, peaktemp INT ) PARTITION BY RANGE (logdate); CREATE TABLE m_2024m01 PARTITION OF measurement FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'); -- List partitioning CREATE TABLE orders (id BIGSERIAL, region TEXT, amount NUMERIC) PARTITION BY LIST (region); CREATE TABLE orders_us PARTITION OF orders FOR VALUES IN ('US', 'CA'); -- Hash partitioning CREATE TABLE users (id BIGSERIAL, email TEXT) PARTITION BY HASH (id); CREATE TABLE users_p0 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 0);
Views & Materialized Views
CREATE VIEW active_users AS SELECT id, email, name FROM users WHERE active = TRUE; -- Materialized view (cached results) CREATE MATERIALIZED VIEW sales_summary AS SELECT DATE_TRUNC('month', created_at) AS month, SUM(amount) AS total, COUNT(*) AS cnt FROM orders GROUP BY 1; CREATE UNIQUE INDEX ON sales_summary (month); REFRESH MATERIALIZED VIEW CONCURRENTLY sales_summary;
Replication & High Availability
PostgreSQL provides two primary types of replication to ensure data redundancy, high availability, and load balancing across multiple servers.
Replication Types
| Feature | Physical (Streaming) | Logical |
|---|---|---|
| Granularity | Entire Cluster | Individual Tables/DBs |
| Standby State | Read-Only (Hot Standby) | Read-Write allowed |
| PG Version | Must be same | Can be different |
1. Physical (Streaming) Replication
This method sends WAL (Write Ahead Log) records from the primary to the standby. The standby is a bit-for-bit exact copy of the primary.
-- Enable streaming replication
wal_level = replica
max_wal_senders = 10
hot_standby = on
-- Clone primary to standby data directory
pg_basebackup -h primary_ip -D /var/lib/postgresql/data -U replicator -P -R
2. Logical Replication
Uses a "Publish and Subscribe" model. It allows you to replicate specific tables and even transform data during replication.
CREATE PUBLICATION my_pub FOR TABLE orders, products;
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=primary_ip dbname=mydb user=replicator'
PUBLICATION my_pub;
Monitoring Status
-- Check replication lag and status
SELECT * FROM pg_stat_replication;
SELECT * FROM pg_stat_subscription;
Write Ahead Log (WAL) & PITR
The Write Ahead Log (WAL) is the backbone of PostgreSQL data integrity. It ensures that no data is lost in the event of a crash by logging changes before they are written to the actual data files.
What is WAL?
When a transaction occurs, Postgres first writes the change to a WAL file in the
pg_wal directory. If the server crashes, Postgres replays these logs upon restart to restore
the database to a consistent state.
wal_level = replica # Required for archiving/PITR
archive_mode = on # Enables WAL archiving
archive_command = 'cp %p /mnt/server/archive/%f'
Point-in-Time Recovery (PITR)
PITR allows you to restore your database to a specific moment in the past (e.g., "restore to 10:15 AM yesterday"). This requires a full physical backup plus a continuous stream of WAL archives.
restore_command = 'cp /mnt/server/archive/%f %p'
recovery_target_time = '2023-10-27 12:00:00'
recovery_target_action = 'promote'
Database Backups
1. Logical Backups (pg_dump)
Creates a script of SQL commands to recreate the database. Best for smaller databases or moving between major versions.
# Backup a single database
pg_dump mydb > backup.sql
# Backup the entire cluster (roles, globals)
pg_dumpall > full_cluster.sql
2. Physical Backups (pg_basebackup)
Creates a bit-for-bit copy of the database files. This is significantly faster for large databases and is the foundation for Replication and PITR.
# Create a physical base backup
pg_basebackup -h localhost -D /path/to/backup -Ft -z -P
Database Maintenance
Maintenance in PostgreSQL is essential for reclaiming space, updating statistics, and preventing transaction ID wraparound. Proper management ensures the database remains fast and healthy over time.
VACUUM: Reclaiming Space
PostgreSQL uses MVCC (Multi-Version Concurrency Control). When a row is deleted or updated,
the old version remains on disk as a "dead tuple." VACUUM marks this space as reusable.
-- Standard vacuum: reclaims space for reuse (doesn't lock table)
VACUUM users;
-- Full vacuum: shrinks file on disk (requires ACCESS EXCLUSIVE lock)
VACUUM FULL users;
-- Vacuum with Analyze
VACUUM ANALYZE users;
Autovacuum
The autovacuum daemon automates the execution of VACUUM and ANALYZE commands. It
triggers based on the number of inserted, updated, or deleted tuples.
autovacuum = on
autovacuum_vacuum_threshold = 50
autovacuum_vacuum_scale_factor = 0.2 -- trigger at 20% bloat
autovacuum_max_workers = 3
ANALYZE & Statistics
The query planner needs accurate statistics to choose the best execution plan.
ANALYZE collects data about the distribution of values in each column.
ANALYZE orders;
-- Check when statistics were last gathered
SELECT relname, last_analyze, last_autoanalyze
FROM pg_stat_user_tables;
Bloat Management
Table or index "bloat" occurs when dead tuples are not reclaimed quickly enough, causing files to grow excessively. Managing bloat is critical for performance.
Monitoring Bloat
Use the pgstattuple extension to find the exact
percentage of dead space in a table.
SELECT * FROM pgstattuple('my_table');
Fixing Bloat
If VACUUM FULL is too disruptive, use tools like
pg_repack to rebuild tables online without locks.
Monitoring
-- Active sessions SELECT pid, usename, state, now() - query_start AS duration, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY duration DESC; -- Top slow queries (pg_stat_statements) SELECT query, calls, mean_exec_time, total_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10; -- Terminate / cancel SELECT pg_terminate_backend(1234); SELECT pg_cancel_backend(1234);
| View | Purpose |
|---|---|
pg_stat_activity |
Live sessions, queries, waits |
pg_stat_user_tables |
Table scans, inserts, dead tuples |
pg_stat_user_indexes |
Index usage frequency |
pg_stat_statements |
Slow query analysis (extension) |
pg_locks |
Current lock holders and waiters |
pg_stat_replication |
Replication lag per standby |
Administration
Users & Roles
CREATE ROLE read_only; GRANT CONNECT ON DATABASE mydb TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; CREATE USER analyst WITH PASSWORD 'pass'; GRANT read_only TO analyst;
Backup & Restore
# Custom compressed format (recommended) pg_dump -U postgres -Fc mydb > mydb.dump # Parallel dump for large databases pg_dump -U postgres -Fd -j 4 -f ./backup_dir mydb # Restore pg_restore -U postgres -d mydb mydb.dump
Key Configuration
| Parameter | Recommended | Description |
|---|---|---|
shared_buffers |
25% of RAM | Memory for caching data pages |
effective_cache_size |
75% of RAM | Hint to planner about OS cache |
work_mem |
4โ64 MB | Memory per sort/hash operation |
maintenance_work_mem |
256 MB โ 1 GB | Memory for VACUUM, CREATE INDEX |
max_connections |
100โ300 | Use PgBouncer for high concurrency |
Authentication & Security
PostgreSQL client authentication is controlled by the pg_hba.conf file. Each
record defines who can connect, from where, and using which method.
pg_hba.conf Format
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256
host postgres all 192.168.93.0/24 ident
Authentication Methods
| Method | Description | Use Case |
|---|---|---|
trust |
No password needed | Local dev |
scram-sha-256 |
Strong password auth | Production |
peer |
OS user matches DB user | Unix sockets |
oauth |
OAuth 2.0 (PG18) | Cloud SSO |
Roles & Privileges
Management Commands
-- Create user with limited privileges
CREATE ROLE app_user WITH LOGIN PASSWORD 'secure!';
-- Grant table access
GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA public TO app_user;
-- Predefined monitoring role
GRANT pg_monitor TO ops_team;
Essential Extensions
pg_stat_statements
Track execution statistics for all SQL statements. Essential for performance.
pgvector
Vector similarity search for AI/ML and embeddings.
TimescaleDB
Automatic partitioning and optimization for time-series data.
The Admin
"Optimizing data, empowering performance."
Results-driven PostgreSQL DBA & PL/SQL Developer specializing in the design, optimization, and administration of high-performance database environments. I empower organizations by architecting scalable database strategies, significantly reducing query latency, and ensuring continuous data availability through enterprise-grade high-availability solutions.
Core Impact & Expertise:
Reduced query execution time by **40%** for high-traffic e-commerce platforms through advanced tuning.
Ensured **99.9% uptime** by implementing enterprise-grade replication and disaster recovery strategies.
Automated repetitive workflows using **PL/pgSQL**, saving over 10 manual hours weekly.
Achieved **100% data integrity** during massive migrations for government organizations.
Developed complex **Stored Procedures & Triggers** to enforce business logic directly at the database layer.
Architected highly scalable **PL/SQL Packages** and dynamic SQL solutions for flexible data processing.
Resources
PostgreSQL Docs
The official documentation โ current and archived versions, comprehensive reference.
SQL Tips
For more SQL and PL/SQL related topics, visit this site for comprehensive guides.
PostgreSQL Tutorial
postgresqltutorial.com โ step-by-step tutorials from beginner to advanced.
pgAdmin
The most popular open-source administration platform for PostgreSQL.
pgBouncer
Lightweight connection pooler โ essential for high-concurrency production deployments.
PostGIS
Adds support for geographic objects โ the go-to choice for geospatial data.