必须用ROW_NUMBER()生成行号再取模才能判断奇偶行,直接对id等字段取模仅按字段值奇偶筛选,无法实现结果集物理顺序的奇偶行交替;ROW_NUMBER()需配合明确ORDER BY以保证行号稳定。

MOD函数在SQL中如何判断奇偶行
SQL里没有“行号”概念,MOD本身不能直接对“第几行”取模,必须配合窗口函数(如ROW_NUMBER())生成序号后才能用。直接写 WHERE MOD(id, 2) = 1 看似能筛奇数id,但那筛的是id字段值的奇偶性,不是查询结果的物理行序——这点极易混淆。
真正想实现“结果集第1、3、5…行”这类交替筛选,核心是:先编号,再取模。
-
ROW_NUMBER() OVER (ORDER BY ...)是最常用且兼容性较好的编号方式,注意ORDER BY必须明确,否则行号无意义 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持
- 旧版 MySQL(ROW_NUMBER(),需改用变量模拟(但并发下不可靠)
筛选奇数行(第1、3、5…行)的写法
以 PostgreSQL/MySQL 8.0+ 为例,给结果集按指定顺序编号后取模:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn FROM orders ) t WHERE MOD(t.rn, 2) = 1;
关键点:
-
MOD(t.rn, 2) = 1表示第1、3、5…行(从1开始计数),不是0、2、4… - 若用
MOD(t.rn, 2) = 0就是偶数行(第2、4、6…行) - 别漏掉子查询别名(如上面的
t),否则外部WHERE无法引用rn -
ORDER BY决定“哪行是第1行”,业务上通常要按时间、ID等有业务含义的字段排序,不能裸写ORDER BY NULL
不同数据库的MOD写法差异
MOD 函数名和行为基本一致,但个别场景需注意:
- PostgreSQL / MySQL / SQL Server:都支持
MOD(a, b)和a % b两种写法,后者更简洁 - Oracle:不支持
MOD()作为函数(它有MOD()但行为与数学取模略有不同),推荐用MOD(a, b)—— 实际可用,但要注意负数:Oracle 的MOD(-1,2)返回1,而标准取模应为-1;不过行号rn永远 ≥1,所以安全 - SQLite:只支持
a % b,不支持MOD(a,b)函数调用
所以更稳妥的写法是统一用 % 运算符:
WHERE t.rn % 2 = 1
为什么不能直接对主键或自增ID取模
常见误区:看到表有 id 自增,就写 WHERE id % 2 = 1。这确实能返回奇数id的记录,但不等于“奇数行”——因为:
- 删除过数据后,
id不连续,第1行可能是id=5,第2行是id=7,此时id % 2 = 1会全中,失去“交替”本意 - 查询加了
WHERE status = 'active'后,结果集顺序和原始id顺序已不一致 - 如果
ORDER BY name,那“第1行”由字母顺序决定,跟id完全无关
归根结底:行序(row order)由最终 ORDER BY 决定,不是由存储顺序或主键决定。没显式编号,就没有“第N行”这个东西。
真正需要奇偶交替时,ROW_NUMBER() + % 是目前最通用、最可控的方式,别绕开它去碰运气。

















