# BD 手册：idx_coach_tasks_rb_unique_active

| 字段 | 值 |
|------|-----|
| 数据库 | zqyy_app |
| Schema | biz |
| 表 | coach_tasks |
| 索引名 | idx_coach_tasks_rb_unique_active |
| 类型 | UNIQUE (partial) |
| 创建日期 | 2026-03-25 |
| 迁移脚本 | `db/zqyy_app/migrations/2026-03-25__relationship_building_baseline.sql` |

## 索引定义

```sql
CREATE UNIQUE INDEX idx_coach_tasks_rb_unique_active
    ON biz.coach_tasks (site_id, assistant_id, member_id)
    WHERE task_type = 'relationship_building' AND status = 'active';
```

## 用途

保证每个 `(site_id, assistant_id, member_id)` 组合最多存在 1 条 `status = 'active'` 的 `relationship_building` 任务。

支持 `_generate_baseline_relationship_tasks()` 中的 upsert 操作：
```sql
INSERT INTO biz.coach_tasks (...)
VALUES (...)
ON CONFLICT (site_id, assistant_id, member_id)
    WHERE task_type = 'relationship_building' AND status = 'active'
DO NOTHING
```

## 业务背景

保底 relationship_building 任务：对每个助教，所有确切发生过服务关系（`session_count > 0`）的客户都生成一条 relationship_building 任务。partial unique index 确保幂等性——重复运行不会产生重复任务。

## 影响范围

- 写入方：`task_generator._generate_baseline_relationship_tasks()` — upsert 依赖此索引
- 读取方：`task_manager.get_task_list_v2()` — 查询不直接使用此索引，但受益于去重保证
- 不影响其他 task_type 的任务（partial index 仅覆盖 `relationship_building` + `active`）

## 回滚

```sql
DROP INDEX IF EXISTS biz.idx_coach_tasks_rb_unique_active;
```

回滚后 `_generate_baseline_relationship_tasks()` 的 `ON CONFLICT` 子句会报错（无匹配索引），需同步回滚代码。

## 验证 SQL

```sql
-- 1. 确认索引存在
SELECT indexname, indexdef FROM pg_indexes
WHERE indexname = 'idx_coach_tasks_rb_unique_active';

-- 2. 确认无重复
SELECT site_id, assistant_id, member_id, COUNT(*)
FROM biz.coach_tasks
WHERE task_type = 'relationship_building' AND status = 'active'
GROUP BY site_id, assistant_id, member_id
HAVING COUNT(*) > 1;
-- 应返回 0 行

-- 3. 测试 upsert 幂等性（dry run）
EXPLAIN INSERT INTO biz.coach_tasks
    (site_id, assistant_id, member_id, task_type, status, priority_score)
VALUES (1, 1, 1, 'relationship_building', 'active', 0)
ON CONFLICT (site_id, assistant_id, member_id)
    WHERE task_type = 'relationship_building' AND status = 'active'
DO NOTHING;
```
