Files
gh-jamesrochabrun-skills/skills/query-expert/scripts/optimize_query.sh
2025-11-29 18:48:55 +08:00

197 lines
6.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Query Expert - Query Optimizer
# Analyze and optimize SQL queries
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
NC='\033[0m'
PASS_COUNT=0
WARN_COUNT=0
FAIL_COUNT=0
print_success() {
echo -e "${GREEN}✓ GOOD${NC} $1"
((PASS_COUNT++))
}
print_warning() {
echo -e "${YELLOW}⚠ IMPROVE${NC} $1"
((WARN_COUNT++))
}
print_error() {
echo -e "${RED}✗ ISSUE${NC} $1"
((FAIL_COUNT++))
}
print_info() {
echo -e "${BLUE} INFO${NC} $1"
}
print_section() {
echo ""
echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${MAGENTA}$1${NC}"
echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
echo ""
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ Query Expert - Query Optimizer ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""
if [ -z "$1" ]; then
print_info "Usage: $0 <query-file.sql>"
print_info "Example: $0 slow_query.sql"
exit 1
fi
QUERY_FILE="$1"
if [ ! -f "$QUERY_FILE" ]; then
echo -e "${RED}File not found: $QUERY_FILE${NC}"
exit 1
fi
QUERY=$(cat "$QUERY_FILE")
# Section 1: SELECT * Detection
print_section "1. COLUMN SELECTION"
if echo "$QUERY" | grep -qi "SELECT \*"; then
print_error "Using SELECT * (selects all columns)"
echo " Fix: SELECT only needed columns"
echo " SELECT user_id, name, email FROM users;"
else
print_success "Selecting specific columns"
fi
# Section 2: Index Usage
print_section "2. INDEX OPPORTUNITIES"
if echo "$QUERY" | grep -qi "WHERE"; then
WHERE_COLS=$(echo "$QUERY" | grep -oi "WHERE [^;]*" | grep -o "[a-zA-Z_][a-zA-Z0-9_]*\s*=" | awk '{print $1}')
if [ -n "$WHERE_COLS" ]; then
print_info "Columns in WHERE clause should have indexes:"
for col in $WHERE_COLS; do
echo " CREATE INDEX idx_table_$col ON table($col);"
done
fi
fi
if echo "$QUERY" | grep -qi "JOIN.*ON"; then
print_info "JOIN columns should have indexes:"
echo " CREATE INDEX idx_table_join_col ON table(join_col);"
fi
if echo "$QUERY" | grep -qi "ORDER BY"; then
print_info "ORDER BY columns benefit from indexes:"
echo " Consider composite index with WHERE + ORDER BY columns"
fi
# Section 3: JOIN Analysis
print_section "3. JOIN OPTIMIZATION"
if echo "$QUERY" | grep -qi "LEFT JOIN" && echo "$QUERY" | grep -qi "WHERE"; then
print_warning "LEFT JOIN with WHERE on right table"
echo " Consider using INNER JOIN instead"
fi
if echo "$QUERY" | grep -qi "WHERE.*IN\s*(SELECT"; then
print_error "Using IN with subquery"
echo " Fix: Use EXISTS or JOIN instead"
echo " WHERE EXISTS (SELECT 1 FROM ...)"
fi
# Section 4: Function Usage
print_section "4. FUNCTION ON COLUMNS"
if echo "$QUERY" | grep -Eqi "WHERE.*(LOWER|UPPER|SUBSTRING|DATE|YEAR|MONTH)\s*\("; then
print_error "Function on indexed column in WHERE"
echo " Fix: Use functional index or avoid function"
echo " CREATE INDEX idx_table_lower_col ON table(LOWER(col));"
fi
# Section 5: DISTINCT Usage
print_section "5. DISTINCT USAGE"
if echo "$QUERY" | grep -qi "SELECT DISTINCT"; then
print_warning "Using DISTINCT (potentially expensive)"
echo " Consider: Is DISTINCT necessary?"
echo " Alternative: Use GROUP BY if aggregating"
fi
# Section 6: Subqueries
print_section "6. SUBQUERY OPTIMIZATION"
SUBQUERY_COUNT=$(echo "$QUERY" | grep -oi "SELECT" | wc -l)
if [ "$SUBQUERY_COUNT" -gt 1 ]; then
if echo "$QUERY" | grep -qi "FROM.*SELECT"; then
print_info "Contains subqueries - consider CTEs for readability"
echo " WITH cte AS (SELECT ...) SELECT ... FROM cte"
fi
fi
# Section 7: LIMIT Usage
print_section "7. RESULT SET SIZE"
if ! echo "$QUERY" | grep -qi "LIMIT\|TOP\|FETCH FIRST"; then
print_warning "No LIMIT clause found"
echo " Add LIMIT to prevent large result sets"
echo " SELECT ... LIMIT 100;"
fi
# Section 8: Sorting
print_section "8. SORTING"
if echo "$QUERY" | grep -qi "ORDER BY"; then
if ! echo "$QUERY" | grep -qi "LIMIT"; then
print_warning "ORDER BY without LIMIT"
echo " Consider adding LIMIT to reduce sort cost"
fi
fi
# Summary
echo ""
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ Optimization Summary ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""
echo -e "${GREEN}✓ Good practices: $PASS_COUNT${NC}"
echo -e "${YELLOW}⚠ Improvements: $WARN_COUNT${NC}"
echo -e "${RED}✗ Issues found: $FAIL_COUNT${NC}"
echo ""
TOTAL=$((PASS_COUNT + FAIL_COUNT + WARN_COUNT))
if [ $TOTAL -gt 0 ]; then
SCORE=$(( ((PASS_COUNT * 2 + WARN_COUNT) * 100) / (TOTAL * 2) ))
echo "Query Score: $SCORE%"
echo ""
fi
print_info "Recommended Next Steps:"
echo " 1. Run EXPLAIN ANALYZE on this query"
echo " 2. Create recommended indexes"
echo " 3. Test query performance before/after"
echo " 4. Monitor query in production"
echo ""
print_info "EXPLAIN Commands:"
echo " PostgreSQL: EXPLAIN ANALYZE <query>"
echo " MySQL: EXPLAIN <query>"
echo " MongoDB: db.collection.find().explain('executionStats')"
echo ""
[ $FAIL_COUNT -gt 0 ] && exit 1 || exit 0