必须用row_number()生成行号再对2取模才能准确判断奇偶行,直接对id等字段取模仅反映字段值奇偶性,无法实现结果集物理顺序的奇偶交替;order by不可省略以确保行号稳定。

MOD函数在SQL中怎么判断奇偶行
直接用 MOD(ROW_NUMBER() OVER (...), 2),不是用 MOD(id, 2) —— 后者依赖主键连续且从1开始,实际数据里几乎总不满足。
真正可靠的“第几行”必须靠窗口函数生成序号,再对2取模。结果为1是奇数行,0是偶数行。
-
MOD(ROW_NUMBER() OVER (ORDER BY id), 2) = 1→ 奇数行(第1、3、5…条) -
MOD(ROW_NUMBER() OVER (ORDER BY id), 2) = 0→ 偶数行(第2、4、6…条) - ORDER BY 子句不能省:没有明确排序,
ROW_NUMBER()的结果无意义
MySQL 8.0+ 和 PostgreSQL 中的写法差异
语法一致,但 MySQL 5.7 及更早版本不支持窗口函数,强行用会报错 ERROR 1064。
PostgreSQL 和 MySQL 8.0+ 都支持,示例:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY created_at) AS rn FROM logs ) t WHERE MOD(t.rn, 2) = 1;
- 别名
rn必须显式定义,不能直接在 WHERE 里写MOD(ROW_NUMBER()..., 2)(多数数据库不支持) - MySQL 5.7 或 MariaDB 10.2 以前:只能用变量模拟行号,但并发或复杂查询下不可靠
- SQLite 3.25+ 支持窗口函数;旧版 SQLite 只能靠
rowid猜,风险高
用主键 id 直接 MOD 安不安全
不安全,除非你 100% 确认:id 是自增、无删除、从1开始、未被手动插入过断点值。
- DELETE 过记录 → id 不连续 → 第3行可能是 id=5,
MOD(5, 2)=1但它是物理第3行,不是逻辑奇数位 - INSERT INTO ... VALUES (100), (101) → id 跳变,
MOD(id, 2)完全脱离“行序”语义 - 复合主键或 UUID 主键 →
MOD无意义
如果业务真只要“id 为奇数的记录”,那可以,但那就不是“奇数行”,而是“id 为奇数的记录”——两个概念不能混。
性能和索引影响要注意什么
ROW_NUMBER() OVER 必然触发全表扫描 + 排序,无法利用索引跳过计算。哪怕只查前10条奇数行,也得先给全表编好号。
- 大表慎用:1000万行表执行一次
ROW_NUMBER(),I/O 和内存开销明显 - ORDER BY 字段如果有索引,能加速排序阶段;否则会建临时文件
- 想高效取“前N个奇数行”?不如用应用层分页 + 偶数跳过逻辑,或者加一个
is_odd_row计算列并建索引(仅限支持生成列的数据库如 MySQL 5.7+、PostgreSQL 12+)
真正要稳定取样时,“奇偶行”只是表征,背后常是分批处理或AB测试分流,这时候用哈希(如 MOD(HASH(id), 4) = 0)反而更可控、可复现。










