sql-analyst
Skill6 starsChanged 8 months agoMITView on GitHub
Use this skill when writing SQL queries for data analysis, reporting, or business metrics. Trigger phrases: 'write a SQL query to', 'how do I calculate in SQL', 'build a report using SQL', 'SQL for monthly active users', 'write a retention query'. Not for database administration, schema design, query optimization for production systems, or NoSQL databases.
3 other projects use a version of this.
Adopted by
Most recently taken up by
- Suryanandx/qvac-uitheir own version, first seen Nov 1, 2026
- runxhq/runxtheir own version, first seen Oct 13, 2026
- darker2016/WorkbuddySkillGroups4DSHtheir own version, first seen Aug 14, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- runxhq/runx★ 97same name
- darker2016/WorkbuddySkillGroups4DSH★ 6same name
- Suryanandx/qvac-ui★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository68 other files it gives its agents.
- dataset-curatorSkill
- eval-designerSkill
- model-comparatorSkill
- prompt-engineerSkill
- business-analystSkill
- competitor-analystSkill
- market-researcherSkill
- meeting-facilitatorSkill
- okr-plannerSkill
- pitch-deck-writerSkill
- product-managerSkill
- ux-researcherSkill
- api-designerSkill
- architecture-designerSkill
- code-reviewerSkill
- debuggerSkill
- documentation-writerSkill
- refactorerSkill
- regex-masterSkill
- security-auditorSkill
- sql-expertSkill
- test-writerSkill
- chart-builderSkill
- dashboard-designerSkill
- data-analystSkill
- data-cleanerSkill
- excel-expertSkill
- color-paletteSkill
- design-critiquerSkill
- frontend-designSkill
- ux-writerSkill
- ci-cd-helperSkill
- docker-expertSkill
- kubernetes-helperSkill
- monitoring-setupSkill
- flashcard-makerSkill
- lesson-plannerSkill
- quiz-creatorSkill
- tutorSkill
- docxSkill
- pdfSkill
- pptxSkill
- xlsxSkill
- budget-plannerSkill
- financial-analystSkill
- invoice-generatorSkill
- contract-reviewerSkill
- legal-summarizerSkill
- terms-of-serviceSkill
- decision-makerSkill
- habit-builderSkill
- journalingSkill
- note-takerSkill
- task-plannerSkill
- citation-formatterSkill
- fact-checkerSkill
- literature-reviewerSkill
- summarizerSkill
- web-researcherSkill
- academic-essaySkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 46
Before writing SQL, translate the question into data terms:
- Distinctive: line 158
Use CTEs to break complex queries into readable, testable steps.
- Very specific: line 367
Always alias aggregated columns (SUM(revenue) AS total_revenue) for readability
- Distinctive: line 368
Use CTEs instead of nested subqueries when logic has more than 2 steps
- Very specific: line 369
Use COUNT(DISTINCT user_id) not COUNT(user_id) when counting unique entities
- Very specific: line 370
Use NULLIF(denominator, 0) in division to avoid divide-by-zero errors
- Very specific: line 372
Use DATE_TRUNC() for time grouping—it handles month-end dates correctly
- Distinctive: line 373
Always include a date filter in analytical queries.
- Distinctive: line 381
Not testing CTEs step-by-step before assembling the full query
- Very specific: line 387
Use LIMIT 100 during development.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SQL Analyst
Overview
This skill provides analytical SQL patterns for answering business questions from relational databases. It covers GROUP BY aggregations, window functions, CTEs, subqueries, date functions, and common business metrics queries (revenue, retention, funnel analysis, cohort analysis). The focus is on readable, correct analytical SQL that produces actionable insights—not on DBA-level optimization or schema changes.
When to Use
- You need to query a database to answer a business question
- Building a report, dashboard data source, or ad-hoc analysis
- Calculating business metrics: revenue, retention, MAU, conversion, cohort analysis
- Translating a business question ("how many users came back in month 2?") into SQL
- Understanding what data exists in a table and what it means
When NOT to Use
- Designing or altering database schemas (
CREATE TABLE,ALTER TABLE) - Database administration (indexes, query plan optimization, partitioning)
- Loading/transforming large datasets in production ETL systems
- MongoDB, Cassandra, or other NoSQL databases (different query languages)
- Writing stored procedures or database functions for production systems
Quick Reference
| Pattern | SQL Construct | Use Case |
| Aggregate by group | GROUP BY ... HAVING | Revenue by region, count by category |
| Running total | SUM() OVER (ORDER BY date) | Cumulative revenue |
| Rank within group | RANK() / ROW_NUMBER() OVER (PARTITION BY ...) | Top N per category |
| Period-over-period | LAG() OVER (ORDER BY month) | Month-over-month comparison |
| Deduplication | ROW_NUMBER() OVER (PARTITION BY id ORDER BY updated_at DESC) | Keep most recent record |
| Percentage of total | SUM(val) / SUM(SUM(val)) OVER () | Revenue share by region |
| Multi-step logic | CTE (WITH ...) | Break complex queries into steps |
| Conditional count | COUNT(CASE WHEN cond THEN 1 END) | Count subset within aggregate |
| Date truncation | DATE_TRUNC('month', date) | Aggregate by month/week/quarter |
| Cohort analysis | Self-join or DATEDIFF on first/return events | Retention calculation |
Instructions
Step 1: Understand the Business Question
Before writing SQL, translate the question into data terms:
- What table(s) hold the relevant data?
- What is the grain of each table (one row per what)?
- What filters apply (date range, user segment, product)?
- What is the output shape? (one number, time series, grouped table)
Step 2: Basic Aggregation Patterns
GROUP BY with multiple metrics:
SELECT
region,
product_category,
COUNT(DISTINCT order_id) AS num_orders,
COUNT(DISTINCT customer_id) AS num_customers,
SUM(revenue) AS total_revenue,
AVG(revenue) AS avg_order_value,
SUM(revenue) / COUNT(DISTINCT order_id) AS revenue_per_order
FROM orders
WHERE order_date >= '2024-01-01'
AND status = 'completed'
GROUP BY region, product_category
ORDER BY total_revenue DESC;HAVING — filter aggregated results:
-- Regions with more than 100 orders and avg order value > $500
SELECT
region,
COUNT(*) AS num_orders,
AVG(revenue) AS avg_order_value
FROM orders
GROUP BY region
HAVING COUNT(*) > 100
AND AVG(revenue) > 500;Step 3: Window Functions
Running total (cumulative revenue):
SELECT
order_date,
revenue,
SUM(revenue) OVER (ORDER BY order_date) AS cumulative_revenue
FROM orders
ORDER BY order_date;Period-over-period with LAG():
WITH monthly AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(revenue) AS monthly_revenue
FROM orders
GROUP BY 1
)
SELECT
month,
monthly_revenue,
LAG(monthly_revenue) OVER (ORDER BY month) AS prev_month_revenue,
ROUND(
(monthly_revenue - LAG(monthly_revenue) OVER (ORDER BY month))
/ NULLIF(LAG(monthly_revenue) OVER (ORDER BY month), 0) * 100,
1) AS pct_change
FROM monthly
ORDER BY month;Rank within group (top product per region):
WITH ranked AS (
SELECT
region,
product,
SUM(revenue) AS total_revenue,
RANK() OVER (PARTITION BY region ORDER BY SUM(revenue) DESC) AS rnk
FROM orders
GROUP BY region, product
)
SELECT region, product, total_revenue
FROM ranked
WHERE rnk = 1;Percentage of total per group:
SELECT
region,
SUM(revenue) AS region_revenue,
ROUND(
SUM(revenue) * 100.0 / SUM(SUM(revenue)) OVER (),
1) AS pct_of_total
FROM orders
GROUP BY region
ORDER BY region_revenue DESC;Deduplication — keep most recent record per customer:
WITH deduped AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY updated_at DESC) AS rn
FROM customers
)
SELECT * FROM deduped WHERE rn = 1;Step 4: CTEs (Common Table Expressions)
Use CTEs to break complex queries into readable, testable steps.
-- Monthly revenue + rolling 3-month average + growth flag
WITH monthly_revenue AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(revenue) AS revenue
FROM orders
WHERE status = 'completed'
GROUP BY 1
),
rolling_avg AS (
SELECT
month,
revenue,
AVG(revenue) OVER (
ORDER BY month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS rolling_3m_avg
FROM monthly_revenue
)
SELECT
month,
revenue,
ROUND(rolling_3m_avg, 0) AS rolling_3m_avg,
CASE WHEN revenue > rolling_3m_avg THEN 'Above trend' ELSE 'Below trend' END AS trend_status
FROM rolling_avg
ORDER BY month;Step 5: Business Metrics Queries
Monthly Active Users (MAU):
SELECT
DATE_TRUNC('month', event_date) AS month,
COUNT(DISTINCT user_id) AS mau
FROM user_events
WHERE event_type = 'session_start'
GROUP BY 1
ORDER BY 1;Revenue by cohort (signup month):
WITH user_cohorts AS (
SELECT
user_id,
DATE_TRUNC('month', created_at) AS cohort_month
FROM users
),
monthly_revenue AS (
SELECT
o.customer_id,
DATE_TRUNC('month', o.order_date) AS order_month,
SUM(o.revenue) AS revenue
FROM orders o
GROUP BY 1, 2
)
SELECT
c.cohort_month,
r.order_month,
DATEDIFF('month', c.cohort_month, r.order_month) AS months_since_signup,
COUNT(DISTINCT r.customer_id) AS paying_users,
SUM(r.revenue) AS cohort_revenue
FROM user_cohorts c
JOIN monthly_revenue r ON c.user_id = r.customer_id
GROUP BY 1, 2, 3
ORDER BY 1, 3;Conversion funnel:
SELECT
COUNT(DISTINCT CASE WHEN step = 'view_product' THEN user_id END) AS step1_view,
COUNT(DISTINCT CASE WHEN step = 'add_to_cart' THEN user_id END) AS step2_cart,
COUNT(DISTINCT CASE WHEN step = 'begin_checkout' THEN user_id END) AS step3_checkout,
COUNT(DISTINCT CASE WHEN step = 'purchase' THEN user_id END) AS step4_purchase,
ROUND(
COUNT(DISTINCT CASE WHEN step = 'purchase' THEN user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT CASE WHEN step = 'view_product' THEN user_id END), 0),
1) AS overall_conversion_pct
FROM funnel_events
WHERE event_date >= '2024-01-01';Day-1 / Day-7 / Day-30 retention:
WITH first_seen AS (
SELECT user_id, MIN(DATE(event_date)) AS first_day
FROM events
GROUP BY user_id
),
activity AS (
SELECT DISTINCT user_id, DATE(event_date) AS active_day
FROM events
)
SELECT
f.first_day AS cohort_date,
COUNT(DISTINCT f.user_id) AS cohort_size,
COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '1 day' THEN f.user_id END) AS d1_retained,
COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '7 days' THEN f.user_id END) AS d7_retained,
COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '30 days' THEN f.user_id END) AS d30_retained,
ROUND(COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '1 day' THEN f.user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT f.user_id), 0), 1) AS d1_rate,
ROUND(COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '7 days' THEN f.user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT f.user_id), 0), 1) AS d7_rate,
ROUND(COUNT(DISTINCT CASE WHEN a.active_day = f.first_day + INTERVAL '30 days' THEN f.user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT f.user_id), 0), 1) AS d30_rate
FROM first_seen f
LEFT JOIN activity a ON f.user_id = a.user_id
GROUP BY 1
ORDER BY 1;Step 6: Date Functions Reference
| Function | PostgreSQL | BigQuery | Snowflake | MySQL |
| Truncate to month | DATE_TRUNC('month', d) | DATE_TRUNC(d, MONTH) | DATE_TRUNC('month', d) | DATE_FORMAT(d, '%Y-%m-01') |
| Difference in days | d2 - d1 | DATE_DIFF(d2, d1, DAY) | DATEDIFF(day, d1, d2) | DATEDIFF(d2, d1) |
| Add interval | d + INTERVAL '7 days' | DATE_ADD(d, INTERVAL 7 DAY) | DATEADD(day, 7, d) | DATE_ADD(d, INTERVAL 7 DAY) |
| Today | CURRENT_DATE | CURRENT_DATE() | CURRENT_DATE() | CURDATE() |
| Extract year | EXTRACT(YEAR FROM d) | EXTRACT(YEAR FROM d) | YEAR(d) | YEAR(d) |
Examples
Example 1: Calculate Monthly Active Users (MAU) with MoM Growth
Input: Table user_events(user_id, event_type, event_date) in PostgreSQL.
WITH mau AS (
SELECT
DATE_TRUNC('month', event_date) AS month,
COUNT(DISTINCT user_id) AS mau
FROM user_events
WHERE event_date >= '2023-01-01'
GROUP BY 1
)
SELECT
month,
mau,
LAG(mau) OVER (ORDER BY month) AS prev_mau,
mau - LAG(mau) OVER (ORDER BY month) AS mau_change,
ROUND(
(mau - LAG(mau) OVER (ORDER BY month)) * 100.0
/ NULLIF(LAG(mau) OVER (ORDER BY month), 0),
1) AS mau_growth_pct
FROM mau
ORDER BY month;Output:
month | mau | prev_mau | mau_change | mau_growth_pct
------------|-------|----------|------------|---------------
2023-01-01 | 8420 | null | null | null
2023-02-01 | 9130 | 8420 | 710 | 8.4
2023-03-01 | 10240 | 9130 | 1110 | 12.2Example 2: Build a Cohort Retention Query
Input: Tables users(user_id, created_at) and orders(order_id, customer_id, order_date) in Snowflake.
WITH cohorts AS (
SELECT
user_id,
DATE_TRUNC('month', created_at) AS cohort_month
FROM users
WHERE created_at >= '2023-01-01'
),
order_activity AS (
SELECT
customer_id,
DATE_TRUNC('month', order_date) AS order_month
FROM orders
),
cohort_data AS (
SELECT
c.cohort_month,
DATEDIFF('month', c.cohort_month, oa.order_month) AS month_number,
COUNT(DISTINCT c.user_id) AS retained_users
FROM cohorts c
LEFT JOIN order_activity oa ON c.user_id = oa.customer_id
GROUP BY 1, 2
),
cohort_sizes AS (
SELECT cohort_month, COUNT(*) AS cohort_size
FROM cohorts
GROUP BY 1
)
SELECT
cd.cohort_month,
cs.cohort_size,
cd.month_number,
cd.retained_users,
ROUND(cd.retained_users * 100.0 / cs.cohort_size, 1) AS retention_rate
FROM cohort_data cd
JOIN cohort_sizes cs ON cd.cohort_month = cs.cohort_month
WHERE cd.month_number BETWEEN 0 AND 6
ORDER BY cd.cohort_month, cd.month_number;Output: A retention table showing what % of each monthly signup cohort placed an order in each subsequent month — the foundation of a retention heatmap.
Best Practices
- Always alias aggregated columns (
SUM(revenue) AS total_revenue) for readability - Use CTEs instead of nested subqueries when logic has more than 2 steps
- Use
COUNT(DISTINCT user_id)notCOUNT(user_id)when counting unique entities - Use
NULLIF(denominator, 0)in division to avoid divide-by-zero errors - Filter early (in WHERE, not HAVING) to reduce rows before aggregation
- Use
DATE_TRUNC()for time grouping—it handles month-end dates correctly - Always include a date filter in analytical queries; querying all-time data is rarely needed and always slow
Common Mistakes
COUNT(*)when you meanCOUNT(DISTINCT user_id)—counts duplicate rows- Division without
NULLIF:revenue / sessionscrashes when sessions = 0 - Using HAVING instead of WHERE to filter raw rows (HAVING runs after GROUP BY)
- Forgetting
LEFT JOINvsINNER JOIN: inner join silently drops users with no activity - Comparing dates with string literals that aren't in ISO format:
WHERE date > '01/01/2024'fails in most databases - Not testing CTEs step-by-step before assembling the full query
- Window functions in WHERE clause (not allowed; use a subquery or CTE)
Tips & Tricks
- Build and test CTEs one at a time: write just the first CTE, run it, verify the output, then add the next
EXPLAIN ANALYZE(PostgreSQL) orQUERY PROFILE(Snowflake) to understand slow queries- Use
LIMIT 100during development; remove for final runs QUALIFY ROW_NUMBER() OVER (...) = 1in Snowflake/BigQuery deduplicates without a wrapping CTESELECT * EXCEPT (col1, col2)in BigQuery to exclude specific columns without listing all the rest- Store complex metric definitions as views so they can be reused across reports
Related Skills
- data-analyst
- data-cleaner
- dashboard-designer
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

