quicklite
Version:
A lightweight ORM toolkit for SQLite in Node.js applications
202 lines (153 loc) • 6.37 kB
Markdown
# Query Analyzer (QueryAnalyzer)
`QueryAnalyzer` is a utility class for analyzing SQLite query performance and providing optimization suggestions. It helps developers identify inefficient queries, detect full table scans, analyze index usage, and suggest indexes for complex queries.
> Compatible with better-sqlite3 v11.8.1 and SQLite 3.48.0
## Features
1. **Query Execution Plan Analysis**: Parses SQLite EXPLAIN QUERY PLAN output to identify potential performance issues
2. **Index Usage Detection**: Checks if queries are using available indexes and identifies index scans vs table scans
3. **Performance Suggestions**: Generates optimization suggestions based on query structure and table structure
4. **Execution Time Measurement**: Measures query execution time to help identify slow queries
5. **Index Suggestions**: Automatically suggests indexes based on query patterns
## Basic Usage
### Analyzing SQL Queries
```typescript
import { QueryAnalyzer } from 'quicklite';
import Database from 'better-sqlite3';
const db = new Database('myapp.db');
// Analyze a simple query
const analysis = QueryAnalyzer.analyze(
db,
'SELECT * FROM users WHERE age > 30'
);
console.log('Query:', analysis.sql);
console.log('Execution time:', analysis.executionTime, 'ms');
console.log('Query plan:');
analysis.queryPlan.forEach(p => {
console.log(`- ${p.detail}`);
});
console.log('Suggestions:');
analysis.suggestions.forEach(s => {
console.log(`- ${s}`);
});
// Analyze a parameterized query
const paramQueryAnalysis = QueryAnalyzer.analyze(
db,
'SELECT * FROM users WHERE email = ?',
['user@example.com']
);
```
### Getting Index Suggestions
```typescript
// Get index suggestions for a complex query
const indexSuggestions = QueryAnalyzer.suggestIndices(
db,
`SELECT u.name, o.product, SUM(o.amount) as total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.age > 30
GROUP BY u.id, o.product
ORDER BY total DESC`
);
console.log('Suggested indexes:');
indexSuggestions.forEach(s => {
console.log(`- ${s}`);
});
```
## API Reference
### QueryAnalyzer Class
#### Static Methods
- **analyze(db: Database, sql: string, params?: any[]): QueryAnalysisResult**
Analyzes an SQL query and returns detailed analysis results.
Parameters:
- `db`: Better-SQLite3 database instance
- `sql`: SQL query to analyze
- `params` (optional): Query parameters
Returns: A `QueryAnalysisResult` object containing query plan, execution time, and optimization suggestions
- **analyzePlan(queryPlan: QueryPlan[], sql: string): string[]**
Generates optimization suggestions based on a query plan.
Parameters:
- `queryPlan`: Array of query plan items
- `sql`: SQL query
Returns: Array of suggestion strings
- **extractTableNames(sql: string): string[]**
Extracts table names from an SQL query.
Parameters:
- `sql`: SQL query
Returns: Array of table names
- **suggestIndices(db: Database, sql: string): string[]**
Suggests possible indices for a given SQL query.
Parameters:
- `db`: Better-SQLite3 database instance
- `sql`: SQL query
Returns: Array of index suggestion strings
### Type Definitions
```typescript
// Query plan item
interface QueryPlan {
id: number;
parentId: number | null;
detail: string;
}
// Query analysis result
interface QueryAnalysisResult {
sql: string;
queryPlan: QueryPlan[];
executionTime: number;
suggestions: string[];
}
```
## Types of Performance Suggestions
QueryAnalyzer can provide various types of performance suggestions, including but not limited to:
1. **Missing Index Suggestions**: When a query contains WHERE, JOIN, or ORDER BY clauses without corresponding indexes
2. **Full Table Scan Warnings**: When a query needs to scan an entire table instead of using an index
3. **Temporary Table Alerts**: When a query needs to create temporary tables for sorting or grouping
4. **Compound Index Suggestions**: When multiple conditions in a query could be optimized with a single compound index
5. **Query Restructuring Recommendations**: Recommending potentially more efficient query patterns
## Best Practices
1. **Regularly Analyze Critical Queries**: Use QueryAnalyzer to regularly check the most frequently used and complex queries in your application
2. **Verify Index Effectiveness**: After adding suggested indexes, analyze the query again to confirm performance improvements
3. **Use During Development**: Catch and solve performance issues early, not when they're already in production
4. **Balance Index Count**: Adding indexes improves query speed but increases write overhead and storage space - find the right balance
## Example Usage Scenarios
### Optimizing Data Report Queries
```typescript
// Analyze a complex reporting query
const reportQueryAnalysis = QueryAnalyzer.analyze(
db,
`SELECT
u.department,
COUNT(DISTINCT u.id) as userCount,
SUM(o.amount) as totalAmount
FROM users u
LEFT JOIN orders o ON u.id = o.userId
WHERE u.joinDate > ?
GROUP BY u.department
ORDER BY totalAmount DESC`,
[Date.now() - 90 * 24 * 60 * 60 * 1000] // Last 90 days
);
// Apply suggested indexes
reportQueryAnalysis.suggestions
.filter(s => s.includes('Consider creating an index on'))
.forEach(suggestion => {
const match = suggestion.match(/on\s+(\w+)\.(\w+)/);
if (match) {
const [_, table, column] = match;
const indexName = `idx_${table}_${column}`;
db.exec(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table}(${column})`);
console.log(`Created index: ${indexName}`);
}
});
```
### Monitoring Application Query Performance
```typescript
// Integrate query monitoring in your application
function monitorQuery(db, sql, params = []) {
const analysis = QueryAnalyzer.analyze(db, sql, params);
// Log queries that take more than 100ms
if (analysis.executionTime > 100) {
logger.warn(`Slow query detected: ${sql}`);
logger.warn(`Execution time: ${analysis.executionTime}ms`);
logger.warn(`Optimization suggestions: ${analysis.suggestions.join('\n')}`);
}
return db.prepare(sql).all(params);
}
```