agentleFS
nisarthnisarth/claude-skills-store/skills/data/write-query/SKILL.md

write-query

Skill1 starsChanged 7 months agoMITView on GitHub

Write optimized SQL for your dialect with best practices. Use when translating a natural-language data need into SQL, building a multi-CTE query with joins and aggregations, optimizing a query against a large partitioned table, or getting dialect-specific syntax right. Trigger phrases: "write a query for", "SQL to get", "query to find", "translate this to SQL", "how do I query", "build a query", "natural language to SQL", "I need a query that", "help me write SQL"

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 8 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If context is available (from a data context skill or conversation), use it.

    line 48
  2. Distinctive:

    Does the GROUP BY match the SELECT?: Every non-aggregated column must be in GROUP BY.

    line 212
  3. Distinctive:

    Are joins correct?: Check for fan-out (1:many producing duplicates) or dropped rows (inner vs left).

    line 213
  4. Distinctive:

    Does the grain match the question?: If they asked "per user" but you grouped by user and date, that is wrong.

    line 217
  5. Distinctive:

    Restate the question: Always confirm what you think is being asked before writing SQL.

    line 233
  6. Distinctive:

    Test incrementally: Run each CTE independently before combining.

    line 239
  7. Distinctive:

    Include a sanity check: Add a CTE that counts rows or sums a key metric to verify the result makes sense.

    line 240
  8. Distinctive:

    Provide alternatives: If there are multiple valid interpretations, show the SQL for each.

    line 242

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Write Query Skill

Translate natural language data requests into correct, optimized SQL. Handle ambiguity, apply business context, and produce queries that are readable, performant, and correct.

Workflow

Step 1: Parse the Natural Language Request

Break the request into SQL components:

Natural LanguageSQL Component
"how many"COUNT(*) or COUNT(DISTINCT ...)
"total", "sum of"SUM(column)
"average"AVG(column)
"by", "broken down by", "per"GROUP BY
"where", "only", "just", "for"WHERE or HAVING
"top N", "highest", "largest"ORDER BY ... DESC LIMIT N
"over time", "trend"GROUP BY date_column ORDER BY date_column
"compared to", "vs"Self-join or window function with LAG
"first", "most recent", "latest"ROW_NUMBER() or MIN()/MAX()
"unique", "distinct"COUNT(DISTINCT ...)
"running total"SUM() OVER (ORDER BY ... ROWS UNBOUNDED PRECEDING)
"percentage", "share"Value divided by SUM() OVER ()
"change", "growth"Current minus previous, often with LAG()
"at least", "more than"HAVING COUNT(*) >= N

Step 2: Identify Ambiguities

Common ambiguities to resolve before writing SQL:

  • "users" -- Do they mean all-time users, active users, registered users?
  • "revenue" -- Gross or net? Including or excluding refunds?
  • "last month" -- Calendar month or rolling 30 days?
  • "active" -- Logged in? Made a purchase? Any event?
  • "new" -- First appearance ever, or first in this period?
  • "conversion rate" -- Visitors to signup? Signup to purchase? What denominator?

If context is available (from a data context skill or conversation), use it. Otherwise, state the assumption explicitly in a comment.

Step 3: Build the Query

Follow this structure:

-- Question: [Restate the natural language question]
-- Assumptions: [State any assumptions made]
-- Dialect: [Snowflake/BigQuery/PostgreSQL/etc.]

WITH
-- Step 1: Get the base data with filters
source_data AS (
    SELECT
        relevant_columns
    FROM schema.table
    WHERE standard_filters
      AND date_filter
),

-- Step 2: Intermediate transformations
transformed AS (
    SELECT
        grouping_columns,
        aggregations
    FROM source_data
    GROUP BY grouping_columns
),

-- Step 3: Final calculations and formatting
final AS (
    SELECT
        columns,
        calculated_fields,
        window_functions
    FROM transformed
)

SELECT *
FROM final
ORDER BY sort_column
;

Step 4: Handle Common Query Patterns

Metric Over Time

-- "Show me daily revenue for the last 30 days"
SELECT
    DATE_TRUNC('day', order_date) AS day,
    SUM(amount) AS revenue,
    COUNT(DISTINCT customer_id) AS customers,
    SUM(amount) / NULLIF(COUNT(DISTINCT customer_id), 0) AS revenue_per_customer
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
  AND status = 'completed'
GROUP BY 1
ORDER BY 1
;

Ranking / Top-N

-- "Top 10 products by revenue this quarter"
SELECT
    product_name,
    SUM(quantity) AS units_sold,
    SUM(revenue) AS total_revenue,
    RANK() OVER (ORDER BY SUM(revenue) DESC) AS revenue_rank
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE order_date >= DATE_TRUNC('quarter', CURRENT_DATE)
GROUP BY product_name
ORDER BY total_revenue DESC
LIMIT 10
;

Period-over-Period Comparison

-- "Compare this month to last month by channel"
WITH monthly AS (
    SELECT
        channel,
        DATE_TRUNC('month', order_date) AS month,
        SUM(revenue) AS revenue
    FROM orders
    WHERE order_date >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '2 months'
    GROUP BY 1, 2
)
SELECT
    channel,
    MAX(CASE WHEN month = DATE_TRUNC('month', CURRENT_DATE) THEN revenue END) AS current_month,
    MAX(CASE WHEN month = DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month' THEN revenue END) AS prior_month,
    current_month - prior_month AS absolute_change,
    (current_month - prior_month) / NULLIF(prior_month, 0) AS pct_change
FROM monthly
GROUP BY 1
ORDER BY current_month DESC
;

Cohort Analysis

-- "Show monthly retention by signup cohort"
WITH cohorts AS (
    SELECT
        user_id,
        DATE_TRUNC('month', MIN(created_at)) AS cohort_month
    FROM users
    GROUP BY 1
),
activity AS (
    SELECT DISTINCT
        user_id,
        DATE_TRUNC('month', event_date) AS active_month
    FROM events
)
SELECT
    c.cohort_month,
    DATEDIFF('month', c.cohort_month, a.active_month) AS months_since_signup,
    COUNT(DISTINCT a.user_id) AS active_users,
    COUNT(DISTINCT a.user_id)::FLOAT / MAX(cohort_size.cnt) AS retention_rate
FROM cohorts c
JOIN activity a ON c.user_id = a.user_id AND a.active_month >= c.cohort_month
JOIN (
    SELECT cohort_month, COUNT(*) AS cnt FROM cohorts GROUP BY 1
) cohort_size ON c.cohort_month = cohort_size.cohort_month
GROUP BY 1, 2
ORDER BY 1, 2
;

Funnel Analysis

-- "What's our signup funnel conversion?"
WITH funnel AS (
    SELECT
        COUNT(DISTINCT CASE WHEN step = 'page_view' THEN user_id END) AS page_views,
        COUNT(DISTINCT CASE WHEN step = 'signup_start' THEN user_id END) AS signup_starts,
        COUNT(DISTINCT CASE WHEN step = 'email_verified' THEN user_id END) AS email_verified,
        COUNT(DISTINCT CASE WHEN step = 'first_action' THEN user_id END) AS first_action
    FROM events
    WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
    page_views,
    signup_starts,
    signup_starts::FLOAT / NULLIF(page_views, 0) AS start_rate,
    email_verified,
    email_verified::FLOAT / NULLIF(signup_starts, 0) AS verify_rate,
    first_action,
    first_action::FLOAT / NULLIF(email_verified, 0) AS activation_rate,
    first_action::FLOAT / NULLIF(page_views, 0) AS overall_conversion
FROM funnel
;

Step 5: Validate the Query

Before delivering, check:

  1. Does the GROUP BY match the SELECT? Every non-aggregated column must be in GROUP BY.
  2. Are joins correct? Check for fan-out (1:many producing duplicates) or dropped rows (inner vs left).
  3. Are filters in the right place? WHERE vs HAVING, and applied in the right CTE.
  4. Is division by zero handled? Wrap denominators with NULLIF(x, 0).
  5. Are date boundaries correct? >= start and < end for date ranges (avoid BETWEEN for timestamps).
  6. Does the grain match the question? If they asked "per user" but you grouped by user and date, that is wrong.

Output Format

-- Question: [Natural language question restated]
-- Dialect: [Target SQL dialect]
-- Tables: [Tables used]
-- Assumptions: [Any assumptions made]
-- Expected output: [Description of columns and grain]

[The SQL query]

Best Practices

  1. Restate the question. Always confirm what you think is being asked before writing SQL.
  2. Name CTEs clearly. daily_revenue not cte1. A reader should understand the query from CTE names alone.
  3. Comment non-obvious logic. Explain business rules, not SQL syntax.
  4. Handle edge cases. What happens with nulls? What if the date range has no data?
  5. Use consistent formatting. One clause per line, aligned keywords, indented subqueries.
  6. Prefer explicit to implicit. LEFT JOIN not LEFT OUTER JOIN ambiguity. CAST over implicit coercion.
  7. Test incrementally. Run each CTE independently before combining.
  8. Include a sanity check. Add a CTE that counts rows or sums a key metric to verify the result makes sense.
  9. Consider performance. Note when a query might be slow and suggest optimizations (partitioning, pre-aggregation).
  10. Provide alternatives. If there are multiple valid interpretations, show the SQL for each.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.