SQL无内置行号,需用ROW_NUMBER() OVER(ORDER BY col)生成逻辑序号再取模;奇数行写法为MOD(rn,2)=1,须用子查询或CTE包裹才能在WHERE中引用rn别名。

SQL里没有“行号”概念,得先造一个
直接用 MOD(row_number, 2) 是错的——因为标准 SQL 表本身不保证物理顺序,也没有内置的“第几行”标识。必须先用窗口函数生成逻辑行号,再对它取模。MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持 ROW_NUMBER(),但 SQLite 和旧版 MySQL 不支持,得换思路。
- 正确写法是:
ROW_NUMBER() OVER (ORDER BY some_column)——ORDER BY必须显式指定,否则语法报错或结果不可靠 - 如果只是想按插入顺序取奇偶行,而表里又没时间戳或自增 ID,那就无法可靠实现;数据库不保存“插入序”元信息
- 某些场景下可用主键近似替代(比如
id是自增整数),但前提是它严格连续且未被删除过,否则MOD(id, 2)不等于“第奇/偶行”
取奇数行:MOD(ROW_NUMBER(), 2) = 1
这是最常用也最稳妥的方式,适用于所有支持窗口函数的主流数据库。注意别漏掉子查询或 CTE 包裹,否则 WHERE 不能直接引用 ROW_NUMBER() 别名。
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM users ) t WHERE MOD(t.rn, 2) = 1;
-
MOD()在 PostgreSQL/MySQL 中可用;SQL Server 要用%运算符(即t.rn % 2 = 1) - SQLite 没有
ROW_NUMBER(),只能靠(SELECT COUNT(*) FROM users u2 WHERE u2.id 模拟,性能差,慎用 - ORDER BY 字段如果有重复值,
ROW_NUMBER()仍会强制分配唯一序号,但结果可能和预期“视觉顺序”不一致
偶数行漏掉第一行?检查 ORDER BY 是否稳定
用 MOD(rn, 2) = 0 查偶数行时,如果结果比预期少一行,大概率是 ORDER BY 字段存在重复,导致排序不稳定,进而让 ROW_NUMBER() 分配的序号在不同执行间漂移。
- 解决办法:在
ORDER BY后追加主键,例如ORDER BY created_at, id,确保排序唯一 - 不要依赖
SELECT * FROM t LIMIT 10 OFFSET 1这类“跳过第一行”的土办法——它不是真正的“偶数行”,只是错位采样 - 如果业务真需要固定分页式奇偶切分(比如导出时分两批处理),建议先把带行号的结果写入临时表,再查,避免重复计算
不支持窗口函数的老版本 MySQL 怎么办
MySQL 5.7 及更早版本不支持 ROW_NUMBER(),但可以用变量模拟,不过要注意执行顺序陷阱:
SELECT * FROM ( SELECT *, @row := @row + 1 AS rn FROM users, (SELECT @row := 0) r ORDER BY id ) t WHERE MOD(t.rn, 2) = 1;
- 变量初始化
(SELECT @row := 0)必须写在FROM子句里,不能放在WHERE或SELECT中,否则行为未定义 - MySQL 8.0 默认禁用变量赋值用于序号生成(警告:This is deprecated),升级后务必迁移到
ROW_NUMBER() - 这种写法在多线程并发查询下可能出错,仅限单次脚本或低并发管理操作
实际用的时候,优先确认数据库版本和排序依据是否可靠。行号是算出来的,不是读出来的——这个前提忘掉,后面全白忙。

















