Postgres advanced
Universal AI development toolkit. 74 production-ready skills for every coding agent. Works with Claude Code, Cursor, Codex.
npx -y skills add medy-gribkov/arcana --skill postgres-advancedAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Advanced PostgreSQL patterns including window functions, CTEs, JSONB operations, full-text search, partitioning, and performance optimization with EXPLAIN ANALYZE
SKILL.md
9.5 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it
Advanced PostgreSQL Operations
Expert-level PostgreSQL patterns for production systems: analytical queries, JSON handling, search optimization, and performance tuning.
Window Functions
Perform calculations across row sets without collapsing results.
BAD: Self-join for ranking
-- Inefficient, creates cartesian product
SELECT e1.name, e1.salary, COUNT(e2.salary) as rank
FROM employees e1
LEFT JOIN employees e2 ON e1.salary <= e2.salary
GROUP BY e1.name, e1.salary;
GOOD: ROW_NUMBER and RANK
-- Efficient window function
SELECT
name,
salary,
department,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as row_num,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) as rank,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) as dense_rank
FROM employees;
BAD: Subquery for previous value
-- Poor performance, scalar subquery per row
SELECT
date,
revenue,
(SELECT revenue FROM sales s2
WHERE s2.date < s1.date
ORDER BY date DESC LIMIT 1) as prev_revenue
FROM sales s1;
GOOD: LAG/LEAD for row comparison
-- Single scan with window function
SELECT
date,
revenue,
LAG(revenue, 1) OVER (ORDER BY date) as prev_revenue,
LEAD(revenue, 1) OVER (ORDER BY date) as next_revenue,
revenue - LAG(revenue, 1) OVER (ORDER BY date) as revenue_change
FROM sales;
Running totals and moving averages
-- Cumulative sum and 7-day moving average
SELECT
date,
amount,
SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total,
AVG(amount) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7d,
FIRST_VALUE(amount) OVER (ORDER BY date) as first_amount,
LAST_VALUE(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as last_amount
FROM transactions;
Common Table Expressions (CTEs)
BAD: Nested subqueries
-- Unreadable, hard to maintain
SELECT * FROM (
SELECT * FROM (
SELECT user_id, COUNT(*) as cnt
FROM orders
GROUP BY user_id
) t1 WHERE cnt > 5
) t2 JOIN users ON t2.user_id = users.id;
GOOD: Named CTEs
-- Clear, reusable, optimized by planner
WITH high_value_users AS (
SELECT user_id, COUNT(*) as order_count
FROM orders
WHERE total > 100
GROUP BY user_id
HAVING COUNT(*) > 5
),
user_details AS (
SELECT u.id, u.name, u.email, hvu.order_count
FROM users u
JOIN high_value_users hvu ON u.id = hvu.user_id
)
SELECT * FROM user_details WHERE order_count > 10;
BAD: Unbounded recursive CTE
-- Risk of infinite loop
WITH RECURSIVE tree AS (
SELECT id, parent_id, name FROM categories WHERE id = 1
UNION ALL
SELECT c.id, c.parent_id, c.name
FROM categories c JOIN tree t ON c.parent_id = t.id
)
SELECT * FROM tree;
GOOD: Recursive CTE with depth limit
-- Safe recursion with cycle detection
WITH RECURSIVE category_tree AS (
SELECT id, parent_id, name, 1 as depth, ARRAY[id] as path
FROM categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.parent_id, c.name, ct.depth + 1, ct.path || c.id
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
WHERE ct.depth < 10 AND NOT c.id = ANY(ct.path)
)
SELECT * FROM category_tree ORDER BY path;
JSONB Operations
BAD: No index on JSON queries
-- Table scan on every query
SELECT * FROM products
WHERE metadata->>'brand' = 'Acme';
GOOD: GIN index on JSONB
-- Create GIN index for fast JSON queries
CREATE INDEX idx_products_metadata ON products USING GIN (metadata);
-- Containment operator (uses index)
SELECT * FROM products
WHERE metadata @> '{"brand": "Acme"}';
-- Path queries with index
SELECT * FROM products
WHERE metadata->>'brand' = 'Acme'
AND metadata->'specs'->>'color' = 'red';
JSONB manipulation and aggregation
-- Update nested JSON
UPDATE products
SET metadata = jsonb_set(
metadata,
'{specs,color}',
'"blue"'
)
WHERE id = 123;
-- Aggregate into JSONB
SELECT
category,
jsonb_agg(
jsonb_build_object(
'id', id,
'name', name,
'price', price
)
) as products
FROM products
GROUP BY category;
-- Extract and transform
SELECT
id,
metadata->>'brand' as brand,
(metadata->'specs'->'dimensions'->>'height')::numeric as height,
jsonb_array_elements_text(metadata->'tags') as tag
FROM products;
Full-Text Search
BAD: LIKE pattern matching
-- Cannot use indexes efficiently
SELECT * FROM articles
WHERE content LIKE '%postgresql%'
OR title LIKE '%postgresql%';
GOOD: tsvector with GIN index
-- Create tsvector column
ALTER TABLE articles
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(content, '')), 'B')
) STORED;
-- Create GIN index
CREATE INDEX idx_articles_search ON articles USING GIN (search_vector);
-- Fast full-text search with ranking
SELECT
id,
title,
ts_rank(search_vector, query) as rank
FROM articles,
websearch_to_tsquery('english', 'postgresql performance') as query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;
-- Highlight search terms
SELECT
ts_headline(
'english',
content,
websearch_to_tsquery('english', 'postgresql performance'),
'MaxWords=50, MinWords=25'
) as snippet
FROM articles
WHERE search_vector @@ websearch_to_tsquery('english', 'postgresql performance');
Performance Optimization
BAD: SELECT * in production
-- Fetches unnecessary data, prevents covering indexes
SELECT * FROM orders WHERE status = 'pending';
GOOD: Specific columns with covering index
-- Covering index (index-only scan)
CREATE INDEX idx_orders_status_covering
ON orders (status)
INCLUDE (id, created_at, total);
SELECT id, created_at, total
FROM orders
WHERE status = 'pending';
Partial indexes for common queries
-- Index only active records
CREATE INDEX idx_users_active_email
ON users (email)
WHERE deleted_at IS NULL;
-- Index only recent orders
CREATE INDEX idx_orders_recent
ON orders (created_at DESC)
WHERE created_at > NOW() - INTERVAL '90 days';
EXPLAIN ANALYZE interpretation
-- Get actual execution plan
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5;
-- Look for:
-- 1. Sequential Scans on large tables → add indexes
-- 2. High "Rows Removed by Filter" → tighten WHERE clause
-- 3. Nested Loop on large datasets → check join conditions
-- 4. High "Buffers: shared hit" vs "read" → cache hit ratio
Table Partitioning
-- Range partitioning by date
CREATE TABLE events (
id BIGSERIAL,
event_type VARCHAR(50),
data JSONB,
created_at TIMESTAMP NOT NULL
) PARTITION BY RANGE (created_at);
-- Create partitions
CREATE TABLE events_2024_q1
PARTITION OF events
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE events_2024_q2
PARTITION OF events
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
-- Create default partition
CREATE TABLE events_default
PARTITION OF events DEFAULT;
-- Indexes on partitions
CREATE INDEX idx_events_2024_q1_type
ON events_2024_q1 (event_type);
-- Partition pruning (automatic with PG 11+)
EXPLAIN SELECT * FROM events
WHERE created_at BETWEEN '2024-02-01' AND '2024-02-28';
-- Only scans events_2024_q1 partition
Advanced Features
Advisory locks for coordination
-- Prevent concurrent execution of job
SELECT pg_advisory_lock(12345);
-- Process data...
UPDATE jobs SET status = 'processing' WHERE id = 100;
-- Release lock
SELECT pg_advisory_unlock(12345);
-- Try lock (non-blocking)
DO $$
BEGIN
IF pg_try_advisory_lock(12345) THEN
-- Got lock, do work
RAISE NOTICE 'Processing job';
PERFORM pg_advisory_unlock(12345);
ELSE
RAISE NOTICE 'Job already running';
END IF;
END $$;
Connection pooling with PgBouncer
# pgbouncer.ini
[databases]
mydb = host=localhost dbname=mydb
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
Logical replication for zero-downtime migrations
-- On source database
CREATE PUBLICATION my_publication
FOR TABLE users, orders;
-- On target database
CREATE SUBSCRIPTION my_subscription
CONNECTION 'host=source-db port=5432 dbname=mydb'
PUBLICATION my_publication;
-- Monitor replication lag
SELECT
slot_name,
active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) as lag
FROM pg_replication_slots;
Workflow
- Design queries: Start with CTEs for readability, use window functions for analytics
- Add indexes: GIN for JSONB/tsvector, partial indexes for filtered queries, covering indexes for hot paths
- Analyze performance: Run EXPLAIN ANALYZE, check for sequential scans and high buffer reads
- Optimize schema: Consider partitioning for large time-series tables, use JSONB for flexible schemas
- Monitor production: Track query execution time, connection pool utilization, replication lag
Use window functions for row-based calculations, CTEs for complex queries, JSONB indexes for JSON searches, and tsvector for full-text search. Always EXPLAIN ANALYZE before deploying performance-critical queries.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.