-- 街拍实体键细化:由 (city, year) 改为 (city, year, title)。 -- -- ⚠️ 本文件是**幂等兜底**,最终表达式的常态承载在 2026-09-22-03(它已直接建同一表达式索引)。 -- 之所以必须保留:03 的 `CREATE UNIQUE INDEX IF NOT EXISTS` 只按**索引名**判断存在性, -- 遇到「同名但口径更窄的旧 (city, year) 索引」会静默跳过、留下错误结构 —— -- 只有本文件的 `DROP INDEX IF EXISTS` + 重建能纠正这类存量库。 -- 全新库上重跑本文件也只是 DROP 后原样重建,幂等无害。 -- -- 背景:同城同年可能有多个专题(如 London 2027 Day 2 / Day 3),原键把它们判为同一实体, -- 导致唯一索引不允许共存、且入库查重把第二个专题当成重复直接放弃。 -- -- COALESCE(title, '') 是刻意的:唯一索引中 NULL 互不冲突,若直接用 title, -- 两条 title 为 NULL 的行会被判为不同实体而共存。入库始终会写 title, -- 这里只是把「NULL 也要参与唯一性」这件事钉死。 -- 建索引前先体检(与 03 同一口径 (city, year, COALESCE(title, ''))): -- 直接建索引只会抛原生 23505,这里先给出可读的报错原因。 DO $$ DECLARE dup int; BEGIN SELECT count(*) INTO dup FROM ( SELECT city, year, COALESCE(title, '') FROM street_snaps WHERE is_deleted = 0 GROUP BY 1, 2, 3 HAVING count(*) > 1 ) t; IF dup > 0 THEN RAISE EXCEPTION 'street_snaps 有 % 组重复实体键,请先人工合并再加唯一索引', dup; END IF; END $$; DROP INDEX IF EXISTS uq_ss_entity; CREATE UNIQUE INDEX IF NOT EXISTS uq_ss_entity ON street_snaps (city, year, COALESCE(title, '')) WHERE is_deleted = 0;