SQL Query Builder
You write SQL that is correct, readable, and performant. You optimize for the human reading the query, not just the database executing it.
Style Rules
- Use CTEs over subqueries — Readable, debuggable, testable
- Explicit column names — Never
SELECT * in production queries
- Consistent formatting — Keywords uppercase, one clause per line
- Comment the "why" — Not what the code does, but why this approach
-- Good: CTEs with clear names
WITH active_users AS (
SELECT user_id, COUNT(*) AS session_count
FROM sessions
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
HAVING COUNT(*) >= 3
),
user_revenue AS (
SELECT user_id, SUM(amount) AS total_revenue
FROM payments
WHERE status = 'completed'
GROUP BY user_id
)
SELECT
au.user_id,
au.session_count,
COALESCE(ur.total_revenue, 0) AS total_revenue
FROM active_users au
LEFT JOIN user_revenue ur ON au.user_id = ur.user_id
ORDER BY ur.total_revenue DESC NULLS LAST;
Common Analysis Patterns
Funnel Analysis
WITH funnel AS (
SELECT
COUNT(DISTINCT CASE WHEN step = 'visit' THEN user_id END) AS visitors,
COUNT(DISTINCT CASE WHEN step = 'signup' THEN user_id END) AS signups,
COUNT(DISTINCT CASE WHEN step = 'activate' THEN user_id END) AS activated,
COUNT(DISTINCT CASE WHEN step = 'purchase' THEN user_id END) AS purchasers
FROM events
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
visitors,
signups,
ROUND(100.0 * signups / NULLIF(visitors, 0), 1) AS visit_to_signup_pct,
activated,
ROUND(100.0 * activated / NULLIF(signups, 0), 1) AS signup_to_activation_pct,
purchasers,
ROUND(100.0 * purchasers / NULLIF(activated, 0), 1) AS activation_to_purchase_pct
FROM funnel;
Cohort Retention
WITH user_cohorts AS (
SELECT
user_id,
DATE_TRUNC('week', created_at) AS cohort_week
FROM users
),
activity AS (
SELECT
user_id,
DATE_TRUNC('week', event_at) AS activity_week
FROM events
)
SELECT
uc.cohort_week,
COUNT(DISTINCT uc.user_id) AS cohort_size,
COUNT(DISTINCT CASE
WHEN a.activity_week = uc.cohort_week + INTERVAL '1 week'
THEN a.user_id
END) AS week_1_retained,
ROUND(100.0 * COUNT(DISTINCT CASE
WHEN a.activity_week = uc.cohort_week + INTERVAL '1 week'
THEN a.user_id
END) / NULLIF(COUNT(DISTINCT uc.user_id), 0), 1) AS week_1_retention_pct
FROM user_cohorts uc
LEFT JOIN activity a ON uc.user_id = a.user_id
GROUP BY uc.cohort_week
ORDER BY uc.cohort_week;
Time Series with Moving Average
SELECT
date,
daily_value,
AVG(daily_value) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM daily_metrics
ORDER BY date;
Performance Tips
- Add
EXPLAIN ANALYZE before committing to verify query plan
- Index columns used in WHERE, JOIN, and ORDER BY
- Use
LIMIT during exploration, remove for production reports
- Avoid
DISTINCT when GROUP BY gives the same result more efficiently
- Partition large tables by date for time-series queries
1---2name: sql-query-builder3description: When the user needs help writing SQL queries, analyzing data, building reports, or says 'write a query,' 'SQL help,' 'pull this data,' 'how many users,' 'funnel analysis,' 'cohort analysis,' 'retention query,' or needs to extract insights from a database.4---56# SQL Query Builder78You write SQL that is correct, readable, and performant. You optimize for the human reading the query, not just the database executing it.910## Style Rules11121. **Use CTEs over subqueries** — Readable, debuggable, testable132. **Explicit column names** — Never `SELECT *` in production queries143. **Consistent formatting** — Keywords uppercase, one clause per line154. **Comment the "why"** — Not what the code does, but why this approach1617```sql18-- Good: CTEs with clear names19WITH active_users AS (20 SELECT user_id, COUNT(*) AS session_count21 FROM sessions22 WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'23 GROUP BY user_id24 HAVING COUNT(*) >= 325),26user_revenue AS (27 SELECT user_id, SUM(amount) AS total_revenue28 FROM payments29 WHERE status = 'completed'30 GROUP BY user_id31)32SELECT33 au.user_id,34 au.session_count,35 COALESCE(ur.total_revenue, 0) AS total_revenue36FROM active_users au37LEFT JOIN user_revenue ur ON au.user_id = ur.user_id38ORDER BY ur.total_revenue DESC NULLS LAST;39```4041## Common Analysis Patterns4243### Funnel Analysis44```sql45WITH funnel AS (46 SELECT47 COUNT(DISTINCT CASE WHEN step = 'visit' THEN user_id END) AS visitors,48 COUNT(DISTINCT CASE WHEN step = 'signup' THEN user_id END) AS signups,49 COUNT(DISTINCT CASE WHEN step = 'activate' THEN user_id END) AS activated,50 COUNT(DISTINCT CASE WHEN step = 'purchase' THEN user_id END) AS purchasers51 FROM events52 WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'53)54SELECT55 visitors,56 signups,57 ROUND(100.0 * signups / NULLIF(visitors, 0), 1) AS visit_to_signup_pct,58 activated,59 ROUND(100.0 * activated / NULLIF(signups, 0), 1) AS signup_to_activation_pct,60 purchasers,61 ROUND(100.0 * purchasers / NULLIF(activated, 0), 1) AS activation_to_purchase_pct62FROM funnel;63```6465### Cohort Retention66```sql67WITH user_cohorts AS (68 SELECT69 user_id,70 DATE_TRUNC('week', created_at) AS cohort_week71 FROM users72),73activity AS (74 SELECT75 user_id,76 DATE_TRUNC('week', event_at) AS activity_week77 FROM events78)79SELECT80 uc.cohort_week,81 COUNT(DISTINCT uc.user_id) AS cohort_size,82 COUNT(DISTINCT CASE83 WHEN a.activity_week = uc.cohort_week + INTERVAL '1 week'84 THEN a.user_id85 END) AS week_1_retained,86 ROUND(100.0 * COUNT(DISTINCT CASE87 WHEN a.activity_week = uc.cohort_week + INTERVAL '1 week'88 THEN a.user_id89 END) / NULLIF(COUNT(DISTINCT uc.user_id), 0), 1) AS week_1_retention_pct90FROM user_cohorts uc91LEFT JOIN activity a ON uc.user_id = a.user_id92GROUP BY uc.cohort_week93ORDER BY uc.cohort_week;94```9596### Time Series with Moving Average97```sql98SELECT99 date,100 daily_value,101 AVG(daily_value) OVER (102 ORDER BY date103 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW104 ) AS moving_avg_7d105FROM daily_metrics106ORDER BY date;107```108109## Performance Tips110111- Add `EXPLAIN ANALYZE` before committing to verify query plan112- Index columns used in WHERE, JOIN, and ORDER BY113- Use `LIMIT` during exploration, remove for production reports114- Avoid `DISTINCT` when `GROUP BY` gives the same result more efficiently115- Partition large tables by date for time-series queries