acc-check-query-efficiency
Query Efficiency Analysis
Analyze PHP code for database query efficiency issues.
Detection Patterns
1. SELECT * Usage
// INEFFICIENT: Fetches all columns
$sql = "SELECT * FROM users WHERE id = ?";
$query = "SELECT * FROM orders JOIN products ON ...";
// EFFICIENT: Only needed columns
$sql = "SELECT id, name, email FROM users WHERE id = ?";
// Doctrine partial select
$qb->select('partial u.{id, name, email}');
2. Missing Index Hints
// SLOW: Likely missing index
"SELECT * FROM orders WHERE customer_email = ?"
// Suggest: CREATE INDEX idx_orders_customer_email ON orders(customer_email)
// SLOW: Function on indexed column
"SELECT * FROM users WHERE LOWER(email) = ?"
// Index on `email` won't be used
// SLOW: Leading wildcard
"SELECT * FROM products WHERE name LIKE '%search%'"
// Consider: Full-text search index
3. Unnecessary Joins
// INEFFICIENT: Join when only ID needed
$qb->select('o', 'c')
->from(Order::class, 'o')
->join('o.customer', 'c')
->where('o.id = :id');
// If only order is used, don't join customer
// INEFFICIENT: Joining unused tables
"SELECT p.name FROM products p
JOIN categories c ON p.category_id = c.id
JOIN brands b ON p.brand_id = b.id"
// categories and brands not used in SELECT or WHERE
4. Full Table Scans
// FULL SCAN: No WHERE clause
"SELECT * FROM users"
$repository->findAll(); // Loads entire table
// FULL SCAN: Non-indexed column
"SELECT * FROM logs WHERE message LIKE '%error%'"
// FULL SCAN: OR with different columns
"SELECT * FROM users WHERE email = ? OR phone = ?"
// May not use indexes efficiently
5. Suboptimal WHERE Clauses
// INEFFICIENT: Function in WHERE
"SELECT * FROM orders WHERE YEAR(created_at) = 2024"
// Better: created_at >= '2024-01-01' AND created_at < '2025-01-01'
// INEFFICIENT: Implicit type conversion
"SELECT * FROM users WHERE id = '123'"
// ID is integer, passing string
// INEFFICIENT: NOT IN with many values
"SELECT * FROM products WHERE id NOT IN (1,2,3,...,1000)"
6. ORDER BY Without Index
// SLOW: Sorting on non-indexed column
"SELECT * FROM orders ORDER BY total DESC"
// Consider: CREATE INDEX idx_orders_total ON orders(total)
// SLOW: Multi-column sort without composite index
"SELECT * FROM products ORDER BY category_id, name"
// Consider: CREATE INDEX idx_products_cat_name ON products(category_id, name)
7. LIMIT Without ORDER BY
// UNPREDICTABLE: No guaranteed order
"SELECT * FROM logs LIMIT 10"
// CORRECT: Explicit ordering
"SELECT * FROM logs ORDER BY id DESC LIMIT 10"
8. Large OFFSET Pagination
// SLOW: Large offset
"SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 10000"
// Must scan and discard 10000 rows
// BETTER: Keyset pagination
"SELECT * FROM products WHERE id > :last_id ORDER BY id LIMIT 20"
9. Count Query Issues
// SLOW: Full count
"SELECT COUNT(*) FROM huge_table"
// SLOW: Count with JOIN
"SELECT COUNT(*) FROM orders o
JOIN line_items li ON o.id = li.order_id"
// BETTER: Count only when necessary
// Use estimates for large tables
"SELECT reltuples FROM pg_class WHERE relname = 'huge_table'"
10. Repeated Queries
// INEFFICIENT: Same query multiple times
$user = $repository->find($userId);
// ... later ...
$user = $repository->find($userId); // Same query again
// Doctrine identity map helps, but not across requests
Grep Patterns
# SELECT * usage
Grep: 'SELECT\s+\*\s+FROM' -i --glob "**/*.php"
# LIKE with leading wildcard
Grep: "LIKE\s+['\"]%" --glob "**/*.php"
# Function in WHERE
Grep: "WHERE.*(LOWER|UPPER|YEAR|MONTH|DATE)\s*\(" -i --glob "**/*.php"
# Large OFFSET
Grep: "OFFSET\s+\d{4,}" --glob "**/*.php"
# findAll() usage
Grep: "->findAll\(\)" --glob "**/*.php"
Index Recommendations
-- Single column indexes for WHERE clauses
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_status ON orders(status);
-- Composite indexes for multi-column queries
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- Covering indexes (include SELECT columns)
CREATE INDEX idx_products_cat_name ON products(category_id) INCLUDE (name, price);
-- Partial indexes for filtered queries
CREATE INDEX idx_orders_active ON orders(user_id) WHERE status = 'active';
Severity Classification
| Pattern | Severity |
|---|---|
| Full table scan on large table | π΄ Critical |
| SELECT * with many columns | π Major |
| Missing composite index | π Major |
| Large OFFSET pagination | π Major |
| Function on indexed column | π‘ Minor |
| Unnecessary joins | π‘ Minor |
Output Format
### Query Efficiency: [Description]
**Severity:** π΄/π /π‘
**Location:** `file.php:line`
**Table(s):** users, orders
**Issue:**
[Description of the efficiency problem]
**Query:**
```sql
SELECT * FROM users WHERE LOWER(email) = 'test@example.com'
Optimization:
SELECT id, name, email FROM users WHERE email = 'test@example.com'
Recommended Index:
CREATE INDEX idx_users_email ON users(email);
Expected Improvement: Query time: ~500ms β ~5ms
More from dykyi-roman/awesome-claude-code
psr-overview-knowledge
PHP Standards Recommendations (PSR) overview knowledge base. Provides comprehensive reference for all accepted PSRs including PSR-1,3,4,6,7,11,12,13,14,15,16,17,18,20. Use for PSR selection decisions and compliance audits.
22detect-code-smells
Detects code smells in PHP codebases. Identifies God Class, Feature Envy, Data Clumps, Long Parameter List, Long Method, Primitive Obsession, Message Chains, Inappropriate Intimacy. Generates actionable reports with refactoring recommendations.
15clean-arch-knowledge
Clean Architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Clean Architecture and Hexagonal Architecture audits.
15ddd-knowledge
DDD architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Domain-Driven Design audits.
14testing-knowledge
Testing knowledge base for PHP 8.4 projects. Provides testing pyramid, AAA pattern, naming conventions, isolation principles, DDD testing guidelines, and PHPUnit patterns.
12bug-root-cause-finder
Root cause analysis methods for PHP bugs. Provides 5 Whys technique, fault tree analysis, git bisect guidance, and stack trace parsing.
12