oracle分页不能直接用offset fetch,因11g及更早版本不支持,且dapper默认按sql server语法生成语句导致ora-00933;需手动用rownum两层子查询(兼容所有版本)或显式启用12c+的offset fetch(依赖oracle.manageddataaccess≥12.2并手写sql)。

Oracle分页为什么不能直接用 OFFSET FETCH?
因为 Oracle 12c 之前不支持 OFFSET ... FETCH,而很多生产环境还在用 11g;即使升级到 12c+,Dapper 默认的 SqlMapper 也不自动适配 Oracle 的分页语法,它仍按 SQL Server 风格生成语句,直接套用会报 ORA-00933: SQL command not properly ended。
所以得手动拼写 Oracle 原生分页结构:用 ROWNUM 套两层子查询,或(推荐)用 12c+ 的 OFFSET ... FETCH——但必须显式指定驱动和方言。
- 确认 Oracle 客户端版本:用
Oracle.ManagedDataAccess≥ 12.2 才能可靠支持OFFSET/FETCH - Dapper 不识别数据库类型,
connection.GetType().Name是OracleConnection,但 Dapper 自身无 Oracle 专用分页扩展 - 别依赖第三方 Dapper 扩展包(如 DapperExtensions),它们对 Oracle 分页支持碎片化且更新滞后
手写 ROWNUM 分页:兼容 11g 的安全写法
这是最稳的方案,适用于所有 Oracle 版本。核心是外层查 ROWNUM BETWEEN @start AND @end,内层用 ORDER BY 确保排序稳定——漏掉 ORDER BY 会导致分页结果乱序。
SELECT * FROM (
SELECT ROWNUM rnum, t.* FROM (
SELECT id, name, created_at FROM users WHERE status = :status ORDER BY id
) t WHERE ROWNUM = :startRow
-
:startRow和:endRow要按页码换算:比如第 3 页、每页 20 条 →startRow = (3-1)*20 + 1 = 41,endRow = 3*20 = 60 - 参数名必须用冒号前缀(
:status),Oracle 驱动要求绑定参数用命名而非位置 - 内层子查询的
ORDER BY字段最好有索引,否则全表扫 + 排序性能极差
用 Oracle 12c+ OFFSET FETCH:简洁但需绕过 Dapper 默认行为
Dapper 的 Query<t></t> 会把 SQL 当黑盒执行,所以你可以直接写带 OFFSET/FETCH 的语句,但要注意 Oracle 对关键字大小写和空格敏感——OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 少一个 ROWS 就报错。
SELECT id, name, created_at FROM users WHERE status = :status ORDER BY id OFFSET :skip ROWS FETCH NEXT :take ROWS ONLY
-
:skip是跳过的行数(不是页码),第 5 页 × 每页 15 条 →skip = (5-1)*15 = 60 - 必须显式写
ORDER BY,否则 Oracle 报ORA-00907: missing right parenthesis - 别用 Dapper 的
QueryMultiple或动态 SQL 构建器来拼这个语句——容易漏空格或错大小写,手写字符串更可控
分页总数怎么查?别在同一条 SQL 里硬塞 COUNT(*)
Oracle 对含 OFFSET/FETCH 的语句不支持窗口函数 COUNT(*) OVER() 同时返回数据和总数;而用子查询套 COUNT(*) 会导致全表扫描两次,大表直接卡死。
- 正确做法:用两个独立查询,用 Dapper 的
QueryMultiple一次往返取两结果集 - 第一段查分页数据(带
OFFSET/FETCH或ROWNUM) - 第二段查总数:
SELECT COUNT(*) FROM users WHERE status = :status(条件必须完全一致) - 注意:如果业务允许“总数近似”,可查
user_tables.num_rows视图,但它是统计信息,非实时
分页逻辑里最容易被忽略的是排序字段的唯一性——如果 ORDER BY created_at 有大量重复值,ROWNUM 或 OFFSET 可能跳过或重复某些行,加 id 作为第二排序字段能彻底避免。











