# 查询分析器 (QueryAnalyzer)

`QueryAnalyzer` 是一个用于分析 SQLite 查询性能并提供优化建议的工具类。它可以帮助开发者识别低效查询、检测全表扫描、分析索引使用情况，并为复杂查询提供索引建议。

> 兼容 better-sqlite3 v11.8.1 和 SQLite 3.48.0

## 功能特性

1. **查询执行计划分析**：解析 SQLite EXPLAIN QUERY PLAN 输出，识别可能的性能问题
2. **索引使用检测**：检查查询是否正在使用可用索引，识别索引扫描和表扫描
3. **性能建议生成**：根据查询结构和表结构生成优化建议
4. **执行时间测量**：测量查询执行时间，帮助识别慢查询
5. **索引建议**：基于查询模式自动建议创建索引

## 基本用法

### 分析 SQL 查询

```typescript
import { QueryAnalyzer } from 'quicklite';
import Database from 'better-sqlite3';

const db = new Database('myapp.db');

// 分析简单查询
const analysis = QueryAnalyzer.analyze(
  db,
  'SELECT * FROM users WHERE age > 30'
);

console.log('查询:', analysis.sql);
console.log('执行时间:', analysis.executionTime, 'ms');
console.log('查询计划:');
analysis.queryPlan.forEach(p => {
  console.log(`- ${p.detail}`);
});
console.log('性能建议:');
analysis.suggestions.forEach(s => {
  console.log(`- ${s}`);
});

// 分析带参数的查询
const paramQueryAnalysis = QueryAnalyzer.analyze(
  db,
  'SELECT * FROM users WHERE email = ?',
  ['user@example.com']
);
```

### 获取索引建议

```typescript
// 获取复杂查询的索引建议
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('建议为以下列创建索引:');
indexSuggestions.forEach(s => {
  console.log(`- ${s}`);
});
```

## API 参考

### QueryAnalyzer 类

#### 静态方法

- **analyze(db: Database, sql: string, params?: any[]): QueryAnalysisResult**
  
  分析 SQL 查询并返回详细的分析结果。
  
  参数:
  - `db`: Better-SQLite3 数据库实例
  - `sql`: 要分析的 SQL 查询语句
  - `params` (可选): 查询的参数值
  
  返回: 包含查询计划、执行时间和优化建议的 `QueryAnalysisResult` 对象

- **analyzePlan(queryPlan: QueryPlan[], sql: string): string[]**
  
  根据查询计划生成优化建议。
  
  参数:
  - `queryPlan`: 查询计划数组
  - `sql`: SQL 查询语句
  
  返回: 优化建议字符串数组

- **extractTableNames(sql: string): string[]**
  
  从 SQL 查询中提取表名。
  
  参数:
  - `sql`: SQL 查询语句
  
  返回: 表名字符串数组

- **suggestIndices(db: Database, sql: string): string[]**
  
  为给定的 SQL 查询建议可能的索引。
  
  参数:
  - `db`: Better-SQLite3 数据库实例
  - `sql`: SQL 查询语句
  
  返回: 索引建议字符串数组

### 类型定义

```typescript
// 查询计划项
interface QueryPlan {
  id: number;
  parentId: number | null;
  detail: string;
}

// 查询分析结果
interface QueryAnalysisResult {
  sql: string;
  queryPlan: QueryPlan[];
  executionTime: number;
  suggestions: string[];
}
```

## 性能建议类型

QueryAnalyzer 可以提供多种类型的性能建议，包括但不限于：

1. **缺少索引建议**：当查询包含 WHERE、JOIN 或 ORDER BY 子句但没有相应索引时
2. **全表扫描警告**：当查询需要扫描整个表而不是使用索引时
3. **临时表使用提醒**：当查询需要创建临时表进行排序或分组时
4. **复合索引建议**：当查询中的多个条件可以通过单个复合索引优化时
5. **查询重构建议**：推荐可能更高效的查询写法

## 最佳实践

1. **定期分析关键查询**：使用 QueryAnalyzer 定期检查应用中最常用和最复杂的查询
2. **验证索引效果**：添加建议的索引后，再次使用 QueryAnalyzer 分析以确认性能改进
3. **在开发阶段使用**：尽早发现并解决性能问题，而不是等到生产环境
4. **权衡索引数量**：添加索引会提高查询速度，但也会增加写入开销和存储空间，需要合理平衡

## 示例应用场景

### 优化数据报表查询

```typescript
// 分析复杂的报表查询
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] // 过去90天
);

// 应用建议的索引
reportQueryAnalysis.suggestions
  .filter(s => s.includes('考虑为以下列创建索引'))
  .forEach(suggestion => {
    const match = suggestion.match(/为\s+(\w+)\.(\w+)\s+创建索引/);
    if (match) {
      const [_, table, column] = match;
      const indexName = `idx_${table}_${column}`;
      db.exec(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table}(${column})`);
      console.log(`已创建索引: ${indexName}`);
    }
  });
```

### 监控应用查询性能

```typescript
// 在应用中集成查询监控
function monitorQuery(db, sql, params = []) {
  const analysis = QueryAnalyzer.analyze(db, sql, params);
  
  // 记录耗时超过100ms的查询
  if (analysis.executionTime > 100) {
    logger.warn(`慢查询检测: ${sql}`);
    logger.warn(`执行时间: ${analysis.executionTime}ms`);
    logger.warn(`优化建议: ${analysis.suggestions.join('\n')}`);
  }
  
  return db.prepare(sql).all(params);
}
``` 