大唔透 發表於 2025-12-21 11:25:37

SQL Server中获取指定范围分页取数的两种方式

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">引言</a></li><li><a href="#_label1">一、推荐方式:OFFSET/FETCH(SQL Server 2012+)</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">基础语法</a></li><li><a href="#_lab2_1_1">示例1:基础分页(适配 FDQuery 手动分批)</a></li><li><a href="#_lab2_1_2">示例2:取前 N 行(简化版)</a></li></ul><li><a href="#_label2">二、兼容低版本:ROW_NUMBER() 函数(SQL Server 2005+)</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_3">基础语法</a></li><li><a href="#_lab2_2_4">示例:适配 FDQuery 分批取数</a></li></ul><li><a href="#_label3">三、关键注意事项(适配 FDQuery 场景)</a></li><ul class="second_class_ul"></ul><li><a href="#_label4">四、对比:两种方式的适用场景</a></li><ul class="second_class_ul"></ul><li><a href="#_label5">五、FDQuery 自动分批适配(无需手动写分页)</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>引言</h2>
<p>在 SQL Server 中获取指定范围的记录(分页/分批取数),核心依赖 <strong>OFFSET/FETCH</strong>(SQL Server 2012+ 推荐)和 <strong>ROW_NUMBER() 函数</strong>(兼容低版本),以下是两种主流方式的详细实现,结合 FDQuery 适配场景说明:</p>
<p class="maodian"><a name="_label1"></a></p><h2>一、推荐方式:OFFSET/FETCH(SQL Server 2012+)</h2>
<p>语法简洁、性能优,是官方推荐的分页语法,适配 FDQuery 分批取数场景。</p>
<p class="maodian"><a name="_lab2_1_0"></a></p><p class="maodian"><a name="_lab2_2_3"></a></p><h3>基础语法</h3>
<div class="jb51code"><pre class="brush:sql;">SELECT 字段列表
FROM 表名
ORDER BY 排序字段 -- 必须指定 ORDER BY,否则OFFSET/FETCH 无效
OFFSET 偏移量 ROWS -- 跳过前 N 行(偏移量 = (页码-1)*每页行数)
FETCH NEXT 行数 ROWS ONLY; -- 取后续 N 行</pre></div>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>示例1:基础分页(适配 FDQuery 手动分批)</h3>
<div class="jb51code"><pre class="brush:sql;">// 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',
   
);
FDQuery1.Open;
end;

// 调用:加载第3页,每页20行
LoadSQLServerPage(3, 20);</pre></div>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>示例2:取前 N 行(简化版)</h3>
<p>若仅需取前100行,可省略 OFFSET:</p>
<div class="jb51code"><pre class="brush:sql;">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;</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>二、兼容低版本:ROW_NUMBER() 函数(SQL Server 2005+)</h2>
<p>若需兼容 SQL Server 2005/2008 等低版本,用 <code>ROW_NUMBER()</code> 生成行号,再筛选行号范围。</p>
<h3>基础语法</h3>
<div class="jb51code"><pre class="brush:sql;">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;</pre></div>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>示例:适配 FDQuery 分批取数</h3>
<div class="jb51code"><pre class="brush:sql;">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',
   
);
FDQuery1.Open;
end;

// 调用:加载第2页,每页15行
LoadSQLServerByRowNum(2, 15);</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>三、关键注意事项(适配 FDQuery 场景)</h2>
<p><strong>必须排序</strong>:OFFSET/FETCH 和 ROW_NUMBER() 都依赖 <code>ORDER BY</code>,否则行号/偏移量无意义,建议用主键(如 id)或索引字段排序,提升性能;</p>
<p><strong>性能优化</strong>:</p>
<ol><li>给排序字段加索引(如 <code>CREATE INDEX idx_student_id ON t_student(id)</code>);</li><li>避免 <code>SELECT *</code>,仅查询需要的字段,减少数据传输;</li></ol>
<p><strong>边界处理</strong>:若偏移量超过总记录数,查询返回空结果,可先查总记录数再判断:</p>
<div class="jb51code"><pre class="brush:sql;">-- 查总记录数(适配 FDQuery 统计总数)
SELECT COUNT(*) FROM t_student WHERE grade = '2025级';</pre></div>
<p><strong>FDQuery 分批适配</strong>:</p>
<ol><li>若用「自动分批」(FetchOptions.RowsetSize),FDQuery 会自动拼接 OFFSET/FETCH 语法(无需手动写分页);</li><li>若用「手动分页控件」,优先用 OFFSET/FETCH 语法(代码更简洁)。</li></ol>
<p class="maodian"><a name="_label4"></a></p><h2>四、对比:两种方式的适用场景</h2>
<table><tbody><tr><td colspan="1" rowspan="1">方式</td><td colspan="1" rowspan="1">优点</td><td colspan="1" rowspan="1">缺点</td><td colspan="1" rowspan="1">适用版本</td></tr><tr><td colspan="1" rowspan="1">OFFSET/FETCH</td><td colspan="1" rowspan="1">语法简洁、性能优</td><td colspan="1" rowspan="1">仅支持 SQL Server 2012+</td><td colspan="1" rowspan="1">2012 及以上</td></tr><tr><td colspan="1" rowspan="1">ROW_NUMBER()</td><td colspan="1" rowspan="1">兼容低版本</td><td colspan="1" rowspan="1">嵌套查询,语法稍复杂</td><td colspan="1" rowspan="1">2005 及以上</td></tr></tbody></table>
<p class="maodian"><a name="_label5"></a></p><h2>五、FDQuery 自动分批适配(无需手动写分页)</h2>
<p>若无需手动控制页码,仅需&ldquo;滚动加载&rdquo;分批取数,可直接配置 FDQuery 的 FetchOptions,FireDAC 会自动适配 SQL Server 的分批语法:</p>
<div class="jb51code"><pre class="brush:sql;">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;</pre></div>
<p>如果需要结合&ldquo;多表关联查询+分页&rdquo;&ldquo;带条件筛选的范围取数&rdquo;等场景,可补充具体需求,我会给出针对性示例。</p>
<p>以上就是SQL Server中获取指定范围分页取数的两种方式的详细内容,更多关于SQL Server指定范围分页取数的资料请关注琼殿技术社区其它相关文章!</p>
頁: [1]
查看完整版本: SQL Server中获取指定范围分页取数的两种方式