要用窗口函数row_number()生成行号再取模,才能按结果集物理顺序筛选奇偶行;直接对id等字段取模仅按该字段值奇偶性过滤。

用 MOD() 或 % 判断行号奇偶性时,必须先生成行号
直接对某列值取模(比如 id % 2)不等于“筛选奇数行/偶数行”——那是按 id 值本身的奇偶性过滤。真要按查询结果的**物理顺序位置**(第1行、第2行…)分奇偶,得先用窗口函数造出行号。ROW_NUMBER() OVER () 是最稳妥的选择,尤其当原始表无序或 id 不连续时。
常见错误:写成 SELECT * FROM t WHERE id % 2 = 1,这筛的是 id 为奇数的记录,不是结果集的第1、3、5…行。
-
ROW_NUMBER()保证严格递增且从1开始,不受原始数据排序影响(只要显式指定ORDER BY) - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持
- 如果没指定
ORDER BY,ROW_NUMBER()行为未定义,不同执行可能结果不同
MySQL 中用 % 筛选结果集的奇数行(第1、3、5…行)
MySQL 支持 % 运算符,且 8.0+ 支持窗口函数。注意:不能在 WHERE 子句里直接引用窗口函数别名,需套一层子查询或 CTE。
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM users ) t WHERE t.rn % 2 = 1;
等价写法(CTE,更清晰):
WITH numbered AS ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM users ) SELECT * FROM numbered WHERE rn % 2 = 1;
-
ORDER BY id决定“行号”的分配顺序,换成ORDER BY created_at就是按时间顺序取奇数位 -
rn % 2 = 1→ 奇数行;rn % 2 = 0→ 偶数行 - 旧版 MySQL(
PostgreSQL / SQL Server 中用 MOD() 实现相同逻辑
PostgreSQL 和 SQL Server 都支持 MOD() 函数(SQL Server 2005+),语义和 % 一致,但函数调用形式更显式。注意参数顺序:MOD(n, m) 返回 n 除以 m 的余数。
-- PostgreSQL 示例 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM products ) t WHERE MOD(t.rn, 2) = 1;
- PostgreSQL 中
%也可用(如rn % 2 = 1),但MOD()在跨数据库迁移时可读性略高 - SQL Server 中
MOD()是 T-SQL 函数,而%是运算符,两者完全等价 - 所有场景下,
MOD(0, 2)返回 0,MOD(1, 2)返回 1 —— 所以判断条件仍为= 1或= 0
性能与兼容性提醒:别在大表上盲目加 ROW_NUMBER()
给百万级表加 ROW_NUMBER() OVER (ORDER BY ...) 会强制全局排序,I/O 和内存开销显著。如果只是想“随机抽一半”,用 WHERE RANDOM() (PostgreSQL)或 <code>TABLESAMPLE 更高效。
- 真正需要严格按顺序位置奇偶采样时(比如导出报表隔行加灰底),才用
ROW_NUMBER()+MOD - 若原表已有自增主键且不删数据,
id % 2可作为近似替代,但遇到删除或批量插入就失效 - Oracle 用户注意:
ROWNUM是伪列,不能在WHERE中直接用于过滤(如WHERE ROWNUM % 2 = 1语法错误),必须用ROW_NUMBER()替代











