SQL中无统一MOD函数,MySQL/Oracle支持MOD(a,b),PostgreSQL/SQL Server/SQLite仅支持a%b;判断奇偶应优先用%运算符,并通过ROW_NUMBER()生成序号后取模,不可直接对物理行序操作。

SQL中没有统一的MOD函数,不同数据库写法差异很大
直接用 MOD() 在某些数据库会报错,比如 PostgreSQL 默认不认这个函数名,而 SQLite 里又只支持 % 运算符。关键不是“能不能取模”,而是“你用的是哪个数据库”——MySQL 支持 MOD(a,b) 和 a % b,PostgreSQL 只认 a % b,SQL Server 得用 a % b(且不支持 MOD()),Oracle 同样只支持 %。
所以第一步永远是查清当前环境:SELECT version(); 或看连接字符串里的方言标识。
给行号取模必须先生成行号,不能直接对物理存储序号操作
SQL 表本身无固有“第几行”概念,所谓“偶数行”指按某排序逻辑生成的序号为偶数的记录。如果漏掉 ORDER BY,ROW_NUMBER() 结果不稳定,两次查询可能得到不同“偶数行”。
常见错误写法:WHERE ROW_NUMBER() OVER() % 2 = 0 —— 这语法非法,窗口函数不能直接出现在 WHERE 子句里。
正确路径是子查询或 CTE:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM users ) t WHERE t.rn % 2 = 0;
注意点:
-
OVER (ORDER BY ...)必须明确,不能空着 - 别名
rn要在外部查询中引用,不能在同级WHERE用 - 如果原表有重复
id,需加二级排序(如ORDER BY id, created_at)保证行号唯一
用 % 还是 MOD()?优先选 % 运算符
虽然 MySQL 同时支持两者,但 % 是 SQL 标准运算符,兼容性更好,也更轻量。除非业务代码已大量使用 MOD() 且跨库迁移成本高,否则没必要换。
几个典型场景对比:
- MySQL / SQLite:两种都行,
id % 2 = 0更简洁 - PostgreSQL / Oracle / SQL Server:只能用
%,MOD(id,2)=0会报错(PostgreSQL 有MOD(),但参数顺序是MOD(n,d),和 MySQL 一致;SQL Server 没这个函数) - 如果字段是
NULL,NULL % 2结果仍是NULL,不会匹配= 0,所以偶数行结果里天然不含该行——这点常被忽略
性能隐患:ROW_NUMBER() + 取模在大数据量下很慢
全表加行号本质是扫描+排序,当表超百万行时,即使有索引,ROW_NUMBER() OVER (ORDER BY indexed_col) 仍可能触发临时文件或内存溢出。
替代思路(仅适用于真需要“物理顺序偶数行”的极少数场景):
- 加自增主键且从 1 开始连续:直接
WHERE id % 2 = 0 - 用游标或应用层分页控制:每次取 2 条跳过 1 条,避免大范围编号
- 物化行号:建带
row_num列的冗余表并定期更新(适合离线分析)
最常被忽略的一点:所谓“偶数行”需求,往往真实意图是“抽样”或“分批处理”,这时候用 NTILE(2) 或哈希分桶(如 ABS(HASH(id)) % 2)反而更合理、更可控。

















