连续重复数据指按某列有序排列时相邻多行某字段值完全相同;子查询无法直接解决因其不维护行序、无上下文,需先构造相邻行标识(如窗口函数或变量模拟的grp),再用子查询过滤。

什么是连续重复数据,为什么子查询不容易直接解决
连续重复数据指在按某列(如时间、ID)有序排列时,相邻多行的某个字段值完全相同。比如日志表中连续 3 行 status = 'ERROR',或订单表中按 created_at 排序后连续出现相同的 user_id。子查询本身不维护行序,也不提供“上一行/下一行”的上下文,所以不能靠一个孤立的 SELECT ... WHERE value IN (SELECT ...) 找出“连续”关系——它只能判断存在性,无法表达位置依赖。
关键点在于:必须先构造出可比较的“相邻行标识”,再用子查询或连接去比对。纯子查询方案往往需要嵌套两层以上,且性能差;更可靠的做法是配合窗口函数预处理,再用子查询过滤。
用 ROW_NUMBER() 配合子查询识别连续段
核心思路是:给每条记录打上两个序号——全局序号和按目标字段分组后的组内序号,二者相减得到“连续组标识”。相同标识的行即属于同一连续段。
例如查找连续重复的 product_id(按 order_time 排序):
SELECT product_id, COUNT(*) AS cnt
FROM (
SELECT product_id,
ROW_NUMBER() OVER (ORDER BY order_time)
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY order_time) AS grp
FROM orders
) t
GROUP BY product_id, grp
HAVING COUNT(*) >= 3;
这个 grp 值是关键中间结果。如果后续逻辑必须用子查询(比如在不支持窗口函数的老版本 MySQL 中),就得把这层计算提前固化为临时表或 CTE,再在外层用子查询引用 grp。
MySQL 5.7 或更早版本的替代方案(无窗口函数)
这类环境无法用 ROW_NUMBER(),只能靠自连接或变量模拟序号,但子查询嵌套极易出错。
常见错误写法:SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE t2.value = t1.value AND t2.id = t1.id + 1) —— 这只能找两两相邻,无法扩展到 N 连续,且无法保证“三连”是同一段(可能跨段匹配)。
可行路径(需谨慎):
- 用用户变量
@row := @row + 1和@prev模拟递增序号与前值对比,在派生表中生成grp字段 - 将该派生表作为子查询的来源,外层再按
grp分组统计 - 避免在同一个查询中多次引用该变量派生表——MySQL 5.7 对派生表物化行为不稳定,容易导致
grp错乱 - 务必加
ORDER BY显式排序,变量依赖执行顺序,无ORDER BY时结果不可靠
WHERE 子句里嵌套子查询查连续?基本不可行
想在 WHERE 中写类似 EXISTS (SELECT 1 FROM t b WHERE b.id = a.id + 1 AND b.status = a.status) 来确认“下一行也相同”,这只能验证单次连续,无法确保长度 ≥3,更无法排除中间夹杂其他值的情况。
若强行扩展成三层嵌套(检查 id+1、id+2、id+3),会遇到两个硬伤:
- SQL 标准不保证子查询中
a.id + 1的行一定存在,NOT EXISTS逻辑难写全 - 一旦某段连续长度为 4,该方案会返回 4 次重复结果(起始位置分别是 id, id+1, id+2, id+3),而非聚合后的一条记录
- 性能随 N 指数级下降,N=5 时就要 5 层嵌套,可读性和维护性归零
真正实用的解法永远是先用窗口函数或变量构造出连续组标识,再分组统计——子查询只适合做最后的过滤或关联,别让它承担序列逻辑。











