Skill: goldath/sql-assistant (version 1.0.0)

--- name: sql-assistant description: > Use this skill when you need to write, review, optimize, or debug SQL queries. Covers query construction, performance tuning, index strategy, window functions, CTEs, and common anti-patterns for PostgreSQL, MySQL, and SQLite. --- # SQL 查询优化助手 ## 核心工作流 ### Step 1 — 理解需求 收集上下文： - 数据库类型（PostgreSQL / MySQL / SQLite / SQL Server） - 表结构（DDL 或列描述） - 业务目标（查什么、过滤条件、聚合逻辑） - 数据量级（小表 <10万 / 中表 <1000万 / 大表 >1000万） - 性能问题描述（慢查询？错误结果？） ### Step 2 — 查询构建 #### 基础查询框架 ```sql SELECT col1, col2, agg_func(col3) AS alias FROM table_name t JOIN other_table o ON t.id = o.fk_id WHERE condition GROUP BY col1, col2 HAVING agg_condition ORDER BY alias DESC LIMIT 100; ``` #### CTE 模式（复杂逻辑拆分） ```sql WITH base_data AS ( SELECT user_id, COUNT(*) AS order_count FROM orders WHERE created_at >= '2026-01-01' GROUP BY user_id ), ranked AS ( SELECT *, RANK() OVER (ORDER BY order_count DESC) AS rk FROM base_data ) SELECT * FROM ranked WHERE rk <= 10; ``` ### Step 3 — 性能优化策略 #### 索引策略 ```sql -- 单列索引 CREATE INDEX idx_orders_user ON orders(user_id); -- 复合索引（遵循最左前缀原则） CREATE INDEX idx_orders_user_date ON orders(user_id, created_at); -- 覆盖索引（避免回表） CREATE INDEX idx_orders_cover ON orders(user_id, created_at, status, amount); ``` #### EXPLAIN 分析 ```sql EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT * FROM orders WHERE user_id = 42; ``` 关注指标： - `Seq Scan` → 考虑加索引 - `rows` 估算偏差大 → 需要 ANALYZE - `cost` 高 → 优化 JOIN 顺序或添加索引 - `Buffers: shared hit/read` → 缓存命中率 ### Step 4 — 常见优化模式 #### 分页优化（大表 OFFSET 慢） ```sql -- ❌ 慢：OFFSET 需扫描丢弃前N行 SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 100000; -- ✅ 快：游标分页 SELECT * FROM orders WHERE id > :last_seen_id ORDER BY id LIMIT 20; ``` #### IN 子查询优化 ```sql -- ❌ 可能慢 SELECT * FROM users WHERE id IN (SELECT user_id FROM premium_members); -- ✅ 用 EXISTS 或 JOIN SELECT u.* FROM users u JOIN premium_members pm ON u.id = pm.user_id; ``` #### 避免函数破坏索引 ```sql -- ❌ 函数包装列，索引失效 WHERE YEAR(created_at) = 2026 -- ✅ 范围条件，索引有效 WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01' ``` ### Step 5 — 窗口函数常用模式 ```sql -- 分组内排名 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) -- 累计求和 SUM(amount) OVER (PARTITION BY user_id ORDER BY created_at) -- 环比计算 LAG(revenue, 1) OVER (ORDER BY month) AS prev_month_revenue -- 移动平均 AVG(score) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) ``` ### Step 6 — 查询审查清单 - [ ] SELECT 只取需要的列（避免 SELECT *） - [ ] WHERE 条件列有索引 - [ ] JOIN 条件有索引 - [ ] 大表分页用游标而非 OFFSET - [ ] 聚合前先 WHERE 过滤（减少聚合数据量） - [ ] 复杂逻辑用 CTE 而非嵌套子查询 - [ ] 无 N+1 查询问题 ## 反模式速查 | 反模式 | 修复方式 | |--------|----------| | SELECT * | 显式列出需要的列 | | OFFSET 大分页 | 改用游标/keyset 分页 | | WHERE 列用函数 | 改用范围条件 | | 隐式类型转换 | 确保参数类型匹配 | | 无 LIMIT 的全表扫描 | 加 LIMIT 或索引过滤 | | OR 替代 UNION | 改用 UNION ALL |

path: references/database-features.md
content: # SQL 数据库特性对比与最佳实践

## PostgreSQL vs MySQL vs SQLite 特性对比

| 特性 | PostgreSQL | MySQL 8+ | SQLite |
|------|-----------|----------|--------|
| 窗口函数 | ✅ 完整支持 | ✅ 完整支持 | ✅ 3.25+ |
| CTE (WITH) | ✅ 递归CTE | ✅ | ✅ |
| JSON 支持 | ✅ jsonb（高效） | ✅ | 有限 |
| 全文搜索 | ✅ 内置 | ✅ | 有限 |
| 并发控制 | MVCC（优秀） | MVCC | WAL模式 |
| 最大数据库大小 | 无限制 | 无限制 | 281TB |
| 适用场景 | 复杂查询/分析 | Web应用/OLTP | 嵌入式/移动端 |

---

## PostgreSQL 专属特性

### JSONB 操作
```sql
-- 创建带 JSONB 列的表
CREATE TABLE products (id SERIAL, attrs JSONB);

-- 查询 JSONB 字段
SELECT * FROM products WHERE attrs->>'color' = 'red';
SELECT * FROM products WHERE attrs @> '{"color": "red"}';

-- JSONB 聚合
SELECT attrs->>'category', COUNT(*) FROM products GROUP BY attrs->>'category';

-- 创建 JSONB 索引
CREATE INDEX idx_products_attrs ON products USING GIN(attrs);
```

### 数组类型
```sql
-- 数组列
CREATE TABLE users (tags TEXT[]);
INSERT INTO users VALUES (ARRAY['dev', 'python', 'sql']);

-- 数组查询（包含）
SELECT * FROM users WHERE 'python' = ANY(tags);
SELECT * FROM users WHERE tags @> ARRAY['dev', 'python'];

-- 数组展开
SELECT UNNEST(tags) AS tag FROM users;
```

### 全文搜索
```sql
-- 创建全文索引
CREATE INDEX idx_articles_fts ON articles USING GIN(to_tsvector('english', content));

-- 全文搜索查询
SELECT * FROM articles
WHERE to_tsvector('english', content) @@ plainto_tsquery('english', 'database optimization');
```

---

## MySQL 专属优化

### InnoDB 配置优化
```sql
-- 查看缓冲池大小（建议设为物理内存的70-80%）
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
```

### MySQL 特有 JSON 函数
```sql
-- 提取 JSON 值
SELECT JSON_EXTRACT(config, '$.theme') FROM settings;
SELECT config->>'$.theme' FROM settings;  -- 简写

-- JSON 数组操作
SELECT JSON_ARRAYAGG(name) FROM users WHERE active = 1;
SELECT JSON_OBJECTAGG(id, name) FROM users;
```

### 分区表（大表优化）
```sql
-- 按日期范围分区
CREATE TABLE orders (
  id BIGINT,
  created_at DATE,
  ...
) PARTITION BY RANGE (YEAR(created_at)) (
  PARTITION p2024 VALUES LESS THAN (2025),
  PARTITION p2025 VALUES LESS THAN (2026),
  PARTITION p2026 VALUES LESS THAN (2027)
);
```

---

## 事务与并发控制

### 事务隔离级别
| 级别 | 脏读 | 不可重复读 | 幻读 | 适用场景 |
|------|------|-----------|------|----------|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 极少使用 |
| READ COMMITTED | ❌ | ✅ | ✅ | 多数OLTP |
| REPEATABLE READ | ❌ | ❌ | ✅(MySQL) | MySQL默认 |
| SERIALIZABLE | ❌ | ❌ | ❌ | 高一致性需求 |

PostgreSQL 默认：READ COMMITTED
MySQL InnoDB 默认：REPEATABLE READ

### 乐观锁 vs 悲观锁
```sql
-- 悲观锁（SELECT FOR UPDATE）
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- 乐观锁（版本号）
UPDATE accounts
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = :expected_version;
-- 检查 affected rows = 1，否则重试
```

---

## 数据库设计规范

### 表设计规范
1. 主键：推荐使用 BIGINT SERIAL/AUTO_INCREMENT（非 UUID，避免索引碎片）
2. 时间戳：使用 `created_at TIMESTAMPTZ DEFAULT NOW()` 和 `updated_at`
3. 软删除：添加 `deleted_at TIMESTAMPTZ NULL`（而非物理删除）
4. 字符集：UTF8MB4（MySQL），UTF8（PostgreSQL）

### 命名规范
- 表名：蛇形命名，复数（`user_orders`）
- 列名：蛇形命名（`created_at`）
- 索引：`idx_{表名}_{列名}` 或 `idx_{表名}_{列名}_{列名}`
- 外键：`fk_{子表}_{父表}`

### 必备字段模板
```sql
CREATE TABLE example (
  id          BIGSERIAL PRIMARY KEY,
  -- 业务字段...
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  deleted_at  TIMESTAMPTZ NULL  -- 软删除
);

-- 自动更新 updated_at（PostgreSQL）
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_updated_at
  BEFORE UPDATE ON example
  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
```


path: references/query-optimization.md
content: # SQL 性能调优完整指南

## 查询执行计划解读

### PostgreSQL EXPLAIN 输出解读

```sql
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 1;
```

输出示例：
```
Index Scan using idx_orders_user on orders
  (cost=0.43..8.45 rows=1 width=64)
  (actual time=0.021..0.023 rows=1 loops=1)
  Index Cond: (user_id = 1)
Buffers: shared hit=3
Planning Time: 0.5 ms
Execution Time: 0.05 ms
```

关键字段说明：
| 字段 | 含义 | 优化方向 |
|------|------|----------|
| Seq Scan | 全表扫描 | 添加索引 |
| Index Scan | 索引扫描（回表） | 考虑覆盖索引 |
| Index Only Scan | 仅索引（最优） | 目标状态 |
| Hash Join | 哈希连接 | 大表连接正常 |
| Nested Loop | 嵌套循环 | 小表驱动大表时有效 |
| rows（估算vs实际差异大） | 统计信息不准 | 运行 ANALYZE |

### MySQL EXPLAIN 关键字段

```sql
EXPLAIN SELECT * FROM orders WHERE user_id = 1\G
```

| Extra 字段 | 含义 |
|-----------|------|
| Using index | 覆盖索引，最优 |
| Using where | 过滤条件在存储引擎层 |
| Using filesort | 内存/磁盘排序，考虑索引 |
| Using temporary | 使用临时表，性能警告 |
| NULL | 正常全表扫描 |

---

## 索引设计原则

### 索引选择性
- 选择性 = 不同值数量 / 总行数
- 选择性 > 0.1 的列适合建索引
- 性别、状态等低选择性列不适合单独建索引

### 复合索引最左前缀规则

```sql
-- 索引：(a, b, c)
SELECT * FROM t WHERE a = 1;              -- ✅ 用到索引
SELECT * FROM t WHERE a = 1 AND b = 2;   -- ✅ 用到索引
SELECT * FROM t WHERE b = 2;             -- ❌ 不用索引
SELECT * FROM t WHERE a = 1 AND c = 3;   -- ✅ 仅用到 a 部分
SELECT * FROM t WHERE a > 1 AND b = 2;   -- ✅ a范围查询，b可能用不到
```

### 索引设计口诀
1. **等值查询**列放前面
2. **范围查询**列放后面
3. **ORDER BY**列放最后（与查询顺序一致）
4. **高选择性**列优先

---

## 慢查询诊断流程

### Step 1：开启慢查询日志
```sql
-- MySQL
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 超过1秒记录

-- PostgreSQL (postgresql.conf)
log_min_duration_statement = 1000  -- 毫秒
```

### Step 2：分析慢查询
```bash
# MySQL 慢查询分析工具
pt-query-digest /var/log/mysql/slow.log

# 或用 mysqldumpslow
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
```

### Step 3：EXPLAIN 分析
```sql
EXPLAIN FORMAT=JSON SELECT ...\G  -- MySQL
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT ...;  -- PostgreSQL
```

### Step 4：优化并验证
- 添加索引 → 再次 EXPLAIN 确认使用索引
- 重写查询 → 对比执行时间
- 更新统计信息：`ANALYZE TABLE orders;`（MySQL）或 `ANALYZE orders;`（PG）

---

## 常见业务场景 SQL 模式

### 分组取最新记录（每用户最后一条订单）
```sql
-- 方法1：窗口函数（推荐）
SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) rn
  FROM orders
) t WHERE rn = 1;

-- 方法2：关联子查询
SELECT o.* FROM orders o
WHERE o.created_at = (
  SELECT MAX(created_at) FROM orders WHERE user_id = o.user_id
);
```

### 统计连续登录天数
```sql
WITH login_gaps AS (
  SELECT
    user_id,
    login_date,
    login_date - INTERVAL (ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date)) DAY AS grp
  FROM user_logins
),
streaks AS (
  SELECT user_id, grp, COUNT(*) AS consecutive_days
  FROM login_gaps
  GROUP BY user_id, grp
)
SELECT user_id, MAX(consecutive_days) AS max_streak
FROM streaks
GROUP BY user_id;
```

### 同比环比计算
```sql
SELECT
  month,
  revenue,
  LAG(revenue, 1) OVER (ORDER BY month) AS prev_month,
  ROUND((revenue - LAG(revenue, 1) OVER (ORDER BY month)) * 100.0
        / LAG(revenue, 1) OVER (ORDER BY month), 2) AS mom_growth_pct,
  LAG(revenue, 12) OVER (ORDER BY month) AS same_month_last_year,
  ROUND((revenue - LAG(revenue, 12) OVER (ORDER BY month)) * 100.0
        / LAG(revenue, 12) OVER (ORDER BY month), 2) AS yoy_growth_pct
FROM monthly_revenue
ORDER BY month;
```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)