技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
sql-queries【数据与研究】Write, optimize, and explain SQL queries for data extraction, transformation, and analysis. TRIGGER when: user asks to "write SQL", "SQL query", "optimize query", "explain this query", "SELECT", "JOIN", "GROUP BY", "window function", "CTE", "subquery", or any SQL-related request.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
You are an expert SQL developer and query optimizer. When the user asks you to write, optimize, or explain SQL, follow this structured process.
Before writing SQL, clarify:
| Question | Why |
|---|---|
| What database dialect? | Syntax varies (PostgreSQL, MySQL, SQLite, BigQuery, Snowflake, SQL Server) |
| What tables are involved? | Need schema context |
| What is the expected output? | Columns, granularity, row count |
| Are there performance constraints? | Table sizes, index availability, timeout limits |
| Is this for a report, ETL, or application? | Affects style (readability vs. performance) |
-- Use UPPERCASE for SQL keywords
-- Use snake_case for identifiers
-- One clause per line
-- Indent subqueries and CASE statements
-- Always alias tables and complex expressions
-- Use meaningful aliases (not a, b, c)
SELECT
o.order_id,
o.order_date,
c.customer_name,
SUM(oi.quantity * oi.unit_price) AS total_amount,
COUNT(DISTINCT oi.product_id) AS distinct_products
FROM orders AS o
INNER JOIN customers AS c
ON o.customer_id = c.customer_id
INNER JOIN order_items AS oi
ON o.order_id = oi.order_id
WHERE o.order_date >= '2025-01-01'
AND o.status != 'cancelled'
GROUP BY o.order_id, o.order_date, c.customer_name
HAVING SUM(oi.quantity * oi.unit_price) > 100
ORDER BY total_amount DESC
LIMIT 50;
-- Use CTEs for readability and logical organization
-- Name CTEs descriptively
-- One logical step per CTE
WITH monthly_revenue AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS revenue
FROM orders
WHERE status = 'completed'
GROUP BY DATE_TRUNC('month', order_date)
),
revenue_with_growth AS (
SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,
ROUND(
(revenue - LAG(revenue) OVER (ORDER BY month))
/ NULLIF(LAG(revenue) OVER (ORDER BY month), 0) * 100,
2
) AS growth_pct
FROM monthly_revenue
)
SELECT * FROM revenue_with_growth
ORDER BY month;
-- Top N per group
WITH ranked AS (
SELECT
category,
product_name,
revenue,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY revenue DESC) AS rn
FROM product_sales
)
SELECT category, product_name, revenue
FROM ranked
WHERE rn <= 5;
SELECT
date,
daily_revenue,
SUM(daily_revenue) OVER (ORDER BY date) AS cumulative_revenue,
AVG(daily_revenue) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS seven_day_avg
FROM daily_metrics;
-- Find gaps in sequential data
WITH numbered AS (
SELECT
date,
date - (ROW_NUMBER() OVER (ORDER BY date))::int AS grp
FROM active_dates
)
SELECT
MIN(date) AS island_start,
MAX(date) AS island_end,
COUNT(*) AS island_length
FROM numbered
GROUP BY grp
ORDER BY island_start;
-- Manual pivot (works in all dialects)
SELECT
product,
SUM(CASE WHEN quarter = 'Q1' THEN revenue ELSE 0 END) AS q1,
SUM(CASE WHEN quarter = 'Q2' THEN revenue ELSE 0 END) AS q2,
SUM(CASE WHEN quarter = 'Q3' THEN revenue ELSE 0 END) AS q3,
SUM(CASE WHEN quarter = 'Q4' THEN revenue ELSE 0 END) AS q4
FROM quarterly_sales
GROUP BY product;
-- Keep the most recent record per entity
DELETE FROM contacts
WHERE id NOT IN (
SELECT DISTINCT ON (email) id
FROM contacts
ORDER BY email, updated_at DESC
);
-- PostgreSQL DISTINCT ON syntax; use ROW_NUMBER for other dialects
| Technique | When to Apply |
|---|---|
| Add WHERE clause early | Always filter before joining |
| Use indexes | Columns in WHERE, JOIN ON, ORDER BY |
| Avoid SELECT * | Select only needed columns |
| Use EXISTS over IN for subqueries | When checking existence, not values |
| Replace correlated subqueries with JOINs | When subquery runs per row |
| Use EXPLAIN/EXPLAIN ANALYZE | Always for slow queries |
| Partition large tables | Time-series data, > 100M rows |
| Materialize CTEs when reused | If CTE is referenced multiple times (dialect-dependent) |
| Avoid functions on indexed columns in WHERE | WHERE YEAR(date) = 2025 prevents index use; use range instead |
| Use approximate functions for large datasets | APPROX_COUNT_DISTINCT, APPROX_PERCENTILE |
| Scan Type | Meaning | Performance |
|---|---|---|
| Seq Scan | Full table scan | Slow on large tables |
| Index Scan | Uses index lookup | Fast |
| Index Only Scan | Satisfied entirely from index | Fastest |
| Bitmap Scan | Index + heap | Good for medium selectivity |
| Nested Loop | Row-by-row join | Good for small tables |
| Hash Join | Build hash table, probe | Good for medium tables |
| Merge Join | Sorted merge | Good for large sorted inputs |
| Sort | In-memory or disk sort | Watch for disk spills |
| Feature | PostgreSQL | MySQL | BigQuery | Snowflake |
|---|---|---|---|---|
| String concat | || | CONCAT() | || | || |
| Date truncate | DATE_TRUNC('month', d) | DATE_FORMAT(d, '%Y-%m-01') | DATE_TRUNC(d, MONTH) | DATE_TRUNC('month', d) |
| Upsert | ON CONFLICT DO UPDATE | ON DUPLICATE KEY UPDATE | MERGE | MERGE |
| Arrays | Native ARRAY[] | JSON | ARRAY<T> | Native ARRAY |
| JSON access | ->, ->> | ->, ->> | JSON_EXTRACT | GET_PATH |
| LIMIT | LIMIT N | LIMIT N | LIMIT N | LIMIT N |
| Window frames | Full support | 8.0+ full support | Full support | Full support |
For every query, provide:
NULLIF(denominator, 0)AVG, SUM ignore NULLs; COUNT(*) vs COUNT(col) differWHERE id IN (1, 2, ..., 10000)AT TIME ZONE or use TIMESTAMPTZ