-- 客服名册索引 —— 按 source_created_at 卡"近 N 月在岗"窗口
--
-- 【为什么现有索引不够】20260801150000 建的 roster 索引末列是 `task_date`,
--   而名册判定**只能用 source_created_at**:task_date 含未来排程
--   (生产实测最远 2033 年,DW 侧甚至 2121 年),拿它卡窗口会把早已离职的人判成在岗。
--   末列不匹配 ⇒ 按 source_created_at 过滤会退化成对 166.7 万行逐行 Filter。
--   ⛔ 不删旧索引:它另有 (patientId, taskDate) 之外的用途,且 task_date 本身仍是业务字段。
--
-- 【⚠️ 这份文件必须只有一条语句】
--   Prisma 把整份 migration.sql 用**一次 simple query** 发给 PG;PG 对"一个查询串里多条语句"
--   隐式开事务块,而 CONCURRENTLY 明确禁止在事务块内跑:
--     ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block (25001)
--   随后 _prisma_migrations 会留一条卡死记录(P3018)**堵死整条迁移流水线**(20260728020000 踩过)。
--   ⛔ 所以**不要**往这个文件里加 `SET LOCAL lock_timeout`、不要加注释以外的任何语句、
--     更不要把它和迁移 A/B 合并。分界线是**语句条数**,不是 Prisma 版本。
--
-- 【为什么用 CONCURRENTLY】patient_return_visits 生产 166.7 万行,普通 CREATE INDEX 会
--   ACCESS EXCLUSIVE 锁全表;而回访表正被增量同步持续写入,锁上就是同步阻塞。
--   CONCURRENTLY 全程不锁写,代价是慢一倍且失败会留 INVALID 索引(重跑前需先 DROP)。
--
-- 【失败救援】CONCURRENTLY 失败会留下一个 INVALID 索引,必须先删再重来:
--   DROP INDEX CONCURRENTLY IF EXISTS "patient_return_visits_roster_source_idx";
--   npx prisma migrate resolve --rolled-back 20260802150000_add_return_visit_roster_source_idx

CREATE INDEX CONCURRENTLY IF NOT EXISTS "patient_return_visits_roster_source_idx" ON "patient_return_visits" ("host_id", "tenant_id", "clinic_id", "task_director_id", "source_created_at" DESC);
