SQL Server 中指定范围分页取数详解

SQL Server 中指定范围分页取数详解

在 SQL Server 中获取指定范围的记录(分页/分批取数),核心依赖 OFFSET/FETCH (SQL Server 2012+ 推荐)和 ROW_NUMBER() 函数(兼容低版本),以下是两种主流方式的详细实现,结合 FDQuery 适配场景说明:

一、推荐方式:OFFSET/FETCH(SQL Server 2012+)

语法简洁、性能优,是官方推荐的分页语法,适配 FDQuery 分批取数场景。

基础语法
复制代码
SELECT 字段列表 
FROM 表名 
ORDER BY 排序字段 -- 必须指定 ORDER BY,否则OFFSET/FETCH 无效
OFFSET 偏移量 ROWS -- 跳过前 N 行(偏移量 = (页码-1)*每页行数)
FETCH NEXT 行数 ROWS ONLY; -- 取后续 N 行
示例1:基础分页(适配 FDQuery 手动分批)
复制代码
复制代码
// Delphi + FDQuery 示例:加载第3页,每页20行
procedure TForm1.LoadSQLServerPage(PageIndex: Integer; PageSize: Integer);
var
  OffsetNum: Integer;
begin
  OffsetNum := (PageIndex - 1) * PageSize; // 计算偏移量(第1页偏移0,第2页偏移20...)
  
  FDQuery1.Close;
  FDQuery1.SQL.Clear;
  FDQuery1.SQL.Text := Format(
    'SELECT id, name, class_id, grade ' +
    'FROM t_student ' +
    'ORDER BY id ' + // 必须排序,建议用主键/索引字段
    'OFFSET %d ROWS ' +
    'FETCH NEXT %d ROWS ONLY',
    [OffsetNum, PageSize]
  );
  FDQuery1.Open;
end;

// 调用:加载第3页,每页20行
LoadSQLServerPage(3, 20);
示例2:取前 N 行(简化版)

若仅需取前100行,可省略 OFFSET:

复制代码
复制代码
SELECT TOP 100 id, name FROM t_student ORDER BY id;
-- 等价于
SELECT id, name FROM t_student ORDER BY id OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;

二、兼容低版本:ROW_NUMBER() 函数(SQL Server 2005+)

若需兼容 SQL Server 2005/2008 等低版本,用 ROW_NUMBER() 生成行号,再筛选行号范围。

基础语法
复制代码
SELECT TOP 100 id, name FROM t_student ORDER BY id;
-- 等价于
SELECT id, name FROM t_student ORDER BY id OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;
示例:适配 FDQuery 分批取数
复制代码
复制代码
procedure TForm1.LoadSQLServerByRowNum(PageIndex: Integer; PageSize: Integer);
var
  StartRow, EndRow: Integer;
begin
  StartRow := (PageIndex - 1) * PageSize + 1; // 起始行号(第1页:1,第2页:21...)
  EndRow := PageIndex * PageSize; // 结束行号(第1页:20,第2页:40...)
  
  FDQuery1.Close;
  FDQuery1.SQL.Clear;
  FDQuery1.SQL.Text := Format(
    'SELECT id, name, class_id, grade FROM (' +
    '  SELECT ' +
    '    id, name, class_id, grade, ' +
    '    ROW_NUMBER() OVER (ORDER BY id) AS RowNum ' +
    '  FROM t_student ' +
    ') AS Temp ' +
    'WHERE Temp.RowNum BETWEEN %d AND %d',
    [StartRow, EndRow]
  );
  FDQuery1.Open;
end;

// 调用:加载第2页,每页15行
LoadSQLServerByRowNum(2, 15);

三、关键注意事项(适配 FDQuery 场景)

  1. 必须排序 :OFFSET/FETCH 和 ROW_NUMBER() 都依赖 ORDER BY,否则行号/偏移量无意义,建议用主键(如 id)或索引字段排序,提升性能;

  2. 性能优化:

    1. 给排序字段加索引(如 CREATE INDEX idx_student_id ON t_student(id));

    2. 避免 SELECT *,仅查询需要的字段,减少数据传输;

  3. 边界处理:若偏移量超过总记录数,查询返回空结果,可先查总记录数再判断:

    复制代码

    -- 查总记录数(适配 FDQuery 统计总数) SELECT COUNT(*) FROM t_student WHERE grade = '2025级';

  4. FDQuery 分批适配:

    1. 若用「自动分批」(FetchOptions.RowsetSize),FDQuery 会自动拼接 OFFSET/FETCH 语法(无需手动写分页);

    2. 若用「手动分页控件」,优先用 OFFSET/FETCH 语法(代码更简洁)。

四、对比:两种方式的适用场景

|--------------|----------|----------------------|----------|
| 方式 | 优点 | 缺点 | 适用版本 |
| OFFSET/FETCH | 语法简洁、性能优 | 仅支持 SQL Server 2012+ | 2012 及以上 |
| ROW_NUMBER() | 兼容低版本 | 嵌套查询,语法稍复杂 | 2005 及以上 |

五、FDQuery 自动分批适配(无需手动写分页)

若无需手动控制页码,仅需"滚动加载"分批取数,可直接配置 FDQuery 的 FetchOptions,FireDAC 会自动适配 SQL Server 的分批语法:

复制代码
procedure TForm1.FDQueryAutoBatch;
begin
  FDQuery1.Close;
  FDQuery1.SQL.Text := 'SELECT id, name, class_id FROM t_student ORDER BY id';
  
  with FDQuery1.FetchOptions do
  begin
    Mode := fmAll; // 按需加载
    RowsetSize := 50; // 每批加载50行
    AutoFetchAll := False; // 禁止一次性加载全量
  end;
  
  FDQuery1.Open; // 首次加载50行,滚动到底部自动加载下一批
end;

如果需要结合"多表关联查询+分页""带条件筛选的范围取数"等场景,可补充具体需求,我会给出针对性示例。

相关推荐
小徐不徐说12 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
论文复现现场12 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
Decay__13 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
心梦缘-雪雁15 小时前
SQL Server 查询数据存在哪个表里
数据库
lusklusklusk15 小时前
Python 巡检脚本:进程运行状态、数据库写入时效检查及邮件通知与告警
数据库·python·oracle
GeW16 小时前
产业新优势基础软件的人才底盘,从Linux与数据库开始
linux·数据库
SelectDB16 小时前
Doris Arrow Flight SQL 高速读取协议实战:与 Snowflake 客户端取数链路对照
大数据·数据库·数据分析
曼岛_16 小时前
[图解密码学]第4章 古典密码
java·数据库·密码学
瀚高PG实验室17 小时前
HGDB V9.0.5 .pgpass 免密登录提示 IsEncryptNon-base64 encoding 警告处理
数据库·postgresql·瀚高数据库
龙腾AI白云17 小时前
大模型检索增强生成(RAG)的技术原理与架构设计
数据库·python·机器学习·知识图谱