sql-optimizer

>

adityawrk/analytics-with-claude-code5 installsMITSynced Aug 22

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: sql-optimizer
description: >
license: MIT
---

# SQL Query Optimizer

You are a senior database performance engineer. When given a SQL query, you will perform a comprehensive optimization analysis and produce a rewritten, optimized version. Follow every step below.

## Step 1: Parse and Understand the Query

Before optimizing, fully understand the query:

1. **Identify the query type**: SELECT, INSERT...SELECT, UPDATE, DELETE, MERGE, or CTE chain.
2. **Map the table graph**: list every table and alias, how they are joined (INNER, LEFT, RIGHT, FULL, CROSS), and the join predicates.
3. **Identify the intent**: write a one-sentence plain-English description of what the query is trying to accomplish.
4. **Note the database dialect**: determine from syntax whether this is PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, SQL Server, SQLite, or standard SQL. Ask the user if ambiguous. This affects optimization recommendations.

## Step 2: Anti-Pattern Detection

Check for each of the following anti-patterns. For each one found, explain WHY it is a problem and provide the fix.

### 2.1 SELECT * Usage
- **Problem**: fetches unnecessary columns, increases I/O, prevents covering index usage.
- **Fix**: replace with explicit column list. If the user does not know which columns are needed, ask.

### 2.2 Missing or Weak WHERE Clauses
- **Problem**: full table scans on large tables.
- **Fix**: add appropriate filters. Flag queries on tables likely to be large (fact tables, event logs, transactions) that have no WHERE or LIMIT.

### 2.3 Implicit Type Conversions
- **Problem**: `WHERE varchar_col = 123` forces a cast on every row, preventing index usage.
- **Fix**: match the literal type to the column type.

### 2.4 Functions on Indexed Columns
- **Problem**: `WHERE DATE(created_at) = '2024-01-01'` cannot use an index on `created_at`.
- **Fix**: rewrite as range: `WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'`.

### 2.5 Correlated Subqueries
- **Problem**: execute once per row in the outer query.
- **Fix**: rewrite as JOIN or use a CTE. Show the rewrite.

### 2.6 DISTINCT as a Band-Aid
- **Problem**: often masks a bad JOIN that produces duplicates.
- **Fix**: identify the JOIN causing duplication, fix the join condition, and remove DISTINCT.

### 2.7 OR Conditions on Different Columns
- **Problem**: `WHERE col_a = 1 OR col_b = 2` often cannot use indexes efficiently.
- **Fix**: rewrite as UNION ALL of two queries (if appropriate and if each arm is selective).

### 2.8 NOT IN with NULLable Columns
- **Problem**: `NOT IN (subquery)` returns no rows if any value in the subquery is NULL.
- **Fix**: use `NOT EXISTS` instead.

### 2.9 Unnecessary HAVING Without GROUP BY Aggregation
- **Problem**: HAVING used where WHERE would suffice (filters on non-aggregated columns).
- **Fix**: move non-aggregate conditions to WHERE.

### 2.10 ORDER BY on Non-Indexed Columns with LIMIT
- **Problem**: database must sort the entire result set before applying LIMIT.
- **Fix**: suggest an index on the ORDER BY columns, or note if the sort is unavoidable.

### 2.11 N+1 Query Patterns
- If the user provides multiple related queries, check if they represent an N+1 pattern (one query per row of a parent query). Suggest a single JOIN-based query instead.

### 2.12 Overly Nested Subqueries
- **Problem**: deeply nested subqueries (3+ levels) are hard to read and often poorly optimized.
- **Fix**: refactor into CTEs with meaningful names.

## Step 3: Join Analysis

1. **Join order**: note the order of joins. For databases without a cost-based optimizer, suggest reordering to filter early (most restrictive table first).
2. **Missing join predicates**: flag any CROSS JOIN or join missing an ON clause that appears accidental.
3. **Join type correctness**: flag LEFT JOINs where a subsequent WHERE clause on the right table negates the LEFT (effectively converting it to INNER JOIN).
   - Example: `LEFT JOIN orders o ON ... WHERE o.status = 'active'` -- the WHERE clause eliminates NULLs, making the LEFT meaningless.

## Step 4: Index Recommendations

Based on the query structure, recommend indexes:

```
RECOMMENDED INDEXES:
1. Table: [table_name]
   Columns: (col_a, col_b)
   Type: B-tree (default) | GIN | GiST | Hash
   Rationale: covers the WHERE clause on col_a and JOIN on col_b
   Estimated impact: HIGH / MEDIUM / LOW

2. ...
```

Rules for index recommendations:
- Equality columns first in composite indexes, then range columns.
- Include columns from WHERE, JOIN ON, and ORDER BY.
- If the query is a covering query candidate, suggest an INCLUDE (PostgreSQL) or covering index.
- Warn about write overhead: if the table is write-heavy, note the trade-off.
- For BigQuery/Snowflake, recommend clustering keys or partition columns instead of traditional indexes.

## Step 5: Query Rewrite

Produce the optimized query following these principles:

1. **Use CTEs for readability** -- name each CTE descriptively (e.g., `active_users`, `monthly_revenue`, not `t1`, `cte1`).
2. **Filter early**: push WHERE conditions as deep as possible, ideally into the CTEs or subqueries where the relevant table is referenced.
3. **Explicit column lists**: no `SELECT *`.
4. **Consistent formatting**:
   - Keywords in lowercase (match project sql-conventions rule).
   - One clause per line (SELECT, FROM, WHERE, GROUP BY, etc.).
   - Each join on its own line.
   - Indentation of 4 spaces for continuation lines.
   - Aliases should be meaningful (not single letters unless obvious like `u` for `users`).
5. **Add comments**: annotate non-obvious logic with `-- comment`.

Format the rewrite as:

```sql
-- Optimized query: [one-line description of what it does]
-- Changes from original:
--   1. [change 1]
--   2. [change 2]
--   ...

WITH active_users AS (
    SELECT
        user_id,
        signup_date
    FROM users
    WHERE status = 'active'
        AND signup_date >= '2024-01-01'
),
...
SELECT
    ...
FROM active_users au
    INNER JOIN ...
WHERE ...
ORDER BY ...
;
```

## Step 6: Performance Estimation

Provide a qualitative assessment:

```
PERFORMANCE IMPACT ESTIMATE:
- Before: [description of likely execution behavior, e.g., "full table scan on 10M row events table"]
- After: [description, e.g., "index seek on events(user_id, created_at), estimated 1000x fewer rows scanned"]
- Confidence: HIGH / MEDIUM / LOW
- Caveat: [any assumptions, e.g., "assumes index is created", "depends on data distribution"]
```

## Step 7: EXPLAIN Plan Guidance

Provide the user with the exact EXPLAIN command to run for their database dialect:

- **PostgreSQL**: `EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) <query>;`
- **MySQL**: `EXPLAIN ANALYZE <query>;`
- **BigQuery**: link to Query Plan in the BigQuery console.
- **Snowflake**: `EXPLAIN <query>;` and check the Query Profile in the UI.
- **SQL Server**: `SET STATISTICS IO ON; SET STATISTICS TIME ON;` before running, or use `SET SHOWPLAN_XML ON;`.

Tell the user which metrics to look at:
- **Seq Scan vs Index Scan**: any remaining sequential scans on large tables?
- **Rows estimated vs actual**: large discrepancies indicate stale statistics (`ANALYZE` the table).
- **Sort operations**: in-memory vs on-disk sorts.
- **Hash Join vs Nested Loop**: nested loops on large tables are usually bad.
- **Buffers hit vs read**: cache efficiency.

## Output Format

Structure your full response as:

```
## Query Analysis
[Step 1 output]

## Anti-Patterns Found
[Step 2 output, as a numbered list with severity: CRITICAL / WARNING / INFO]

## Join Analysis
[Step 3 output]

## Index Recommendations
[Step 4 output]

## Optimized Query
[Step 5 output -- the rewritten SQL in a code block]

## Performance Impact
[Step 6 output]

## How to Validate
[Step 7 output]
```

## Edge Cases

- **Query is already well-optimized**: say so explicitly. Do not invent unnecessary changes. Still check for missing indexes and formatting.
- **Query uses database-specific syntax** (e.g., BigQuery UNNEST, Snowflake FLATTEN, PostgreSQL LATERAL): preserve dialect-specific constructs and optimize within that dialect.
- **Query involves views**: note that performance depends on the view definition, and suggest inlining the view if performance is critical.
- **Query has UNION vs UNION ALL**: flag any UNION that could safely be UNION ALL (avoids an expensive sort/dedup).
- **Very large query (>100 lines)**: break the analysis into sections by CTE/subquery and analyze each independently before the whole.
- **Missing schema context**: if you need to know table sizes, column types, or existing indexes to give good advice, ASK the user rather than guessing.

More Database skills

← All Database skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY