一、分页查询的核心概念

  • 定义:将大量数据按 “每页固定条数” 拆分,分批次展示(比如每页显示 10 条,第 1 页显示 1-10 条,第 2 页显示 11-20 条)。
  • 用处:避免一次性加载海量数据,提升查询性能和页面加载速度。
  • 关键参数
    • @CurrentPage:当前要查询的页码(如 “第 2 页”)。
    • @PageSize:每页显示的记录条数(如 “每页 3 条”)。

二、方式 1:ROW_NUMBER () 分页(SQL Server 2012 版本以下适用)

1. 语法定义

ROW_NUMBER() 是一个窗口函数,用于给查询结果的每一行分配一个连续的 “行编号”,结合子查询筛选行编号范围实现分页。

-- 步骤1:定义分页参数
DECLARE @CurrentPage INT;  -- 当前页码
DECLARE @PageSize INT;     -- 每页条数

-- 步骤2:参数赋值
SET @CurrentPage = 1;  -- 示例:查询第1页
SET @PageSize = 3;     -- 示例:每页显示3条

-- 步骤3:分页查询(核心)
SELECT * FROM (
    -- 子查询:给每一行分配行编号RowId
    SELECT 
        ROW_NUMBER() OVER(ORDER BY 排序列 [ASC/DESC]) AS RowId,  -- 按指定列排序后生成行编号
        *  -- 要查询的列(或指定列名)
    FROM 表名
) AS 子查询别名
-- 筛选行编号范围:(当前页-1)*每页条数 +1 ≤ RowId ≤ 当前页*每页条数
WHERE RowId BETWEEN (@CurrentPage - 1) * @PageSize + 1 AND @CurrentPage * @PageSize;

2. 关键语法解释

  • ROW_NUMBER() OVER(ORDER BY 排序列)
    • ROW_NUMBER():生成连续的行编号(从 1 开始)。
    • OVER(ORDER BY 排序列):指定 “按哪一列排序” 后再分配行编号(必须指定排序,否则行编号无意义)。
  • 行编号范围公式:
    • 起始 RowId = (@CurrentPage - 1) * @PageSize + 1(如第 1 页:(1-1)*3+1=1)。
    • 结束 RowId = @CurrentPage * @PageSize(如第 1 页:1*3=3)。
-- 定义参数
DECLARE @CurrentPage INT;
DECLARE @PageSize INT;

-- 赋值:查询第1页,每页3条
SET @CurrentPage = 1;
SET @PageSize = 3;

-- 分页查询(按Age排序)
SELECT * FROM (
    SELECT 
        ROW_NUMBER() OVER(ORDER BY Age) AS RowId,  -- 按Age升序生成行编号
        * 
    FROM Students
) AS S
WHERE RowId BETWEEN (@CurrentPage - 1) * @PageSize + 1 AND @CurrentPage * @PageSize;
  • 结果:返回 Students 表中按 Age 排序后的前 3 条数据(第 1 页)。

三、方式 2:OFFSET...FETCH 分页(SQL Server 2012 版本及以上适用)

1. 语法定义

OFFSET...FETCH 是 SQL Server 2012 新增的简化分页语法,直接指定 “跳过的行数” 和 “获取的行数”,写法更简洁。

-- 步骤1:定义分页参数
DECLARE @CurrentPage INT;  -- 当前页码
DECLARE @PageSize INT;     -- 每页条数

-- 步骤2:参数赋值
SET @CurrentPage = 1;  -- 示例:查询第1页
SET @PageSize = 3;     -- 示例:每页显示3条

-- 步骤3:分页查询(核心)
SELECT *  -- 要查询的列(或指定列名)
FROM 表名
ORDER BY 排序列 [ASC/DESC]  -- 必须指定排序
OFFSET (@CurrentPage - 1) * @PageSize ROWS  -- 跳过“(当前页-1)*每页条数”行
FETCH NEXT @PageSize ROWS ONLY;             -- 获取接下来的“每页条数”行

2. 关键语法解释

  • OFFSET X ROWS:表示 “跳过前 X 行数据”(X = (@CurrentPage - 1) * @PageSize,如第 1 页:(1-1)*3=0,即不跳过)。
  • FETCH NEXT Y ROWS ONLY:表示 “获取接下来的 Y 行数据”(Y = @PageSize,如每页 3 条)。
  • 必须配合 ORDER BY:否则 SQL Server 会报错(分页需要明确的排序规则)。
-- 定义参数
DECLARE @CurrentPage INT;
DECLARE @PageSize INT;

-- 赋值:查询第1页,每页3条
SET @CurrentPage = 1;
SET @PageSize = 3;

-- 分页查询(按StudentId排序)
SELECT *
FROM Students
ORDER BY StudentId  -- 按StudentId升序排序
OFFSET (@CurrentPage - 1) * @PageSize ROWS  -- 跳过0行
FETCH NEXT @PageSize ROWS ONLY;             -- 获取3行
  • 结果:返回 Students 表中按 StudentId 排序后的前 3 条数据(第 1 页)。

四、两种分页方式对比

特性 ROW_NUMBER () 分页 OFFSET...FETCH 分页
适用版本 SQL Server 2005 及以上 SQL Server 2012 及以上
语法复杂度 较复杂(需要子查询) 简洁(直接写在主查询)
排序要求 必须在 OVER () 中指定 ORDER BY 必须在主查询中指定 ORDER BY
性能 数据量大时略逊于 OFFSET...FETCH 性能更优(SQL Server 优化过)
适用场景 低版本 SQL Server 高版本 SQL Server(推荐)

一、游标(Cursor)

1. 定义

游标是 SQL Server 中用于逐行处理查询结果集的数据库对象,它可以将结果集拆分为单行数据,通过 “定位” 和 “读取” 操作逐行处理数据(类似程序中的 “循环遍历”)。

2. 用处

  • 当需要逐行处理数据(而非批量处理)时使用(比如逐行读取数据并插入到临时表)。
  • 适用于复杂的行级逻辑(批量操作无法实现的场景)。

3. 游标语法(完整流程)

游标使用分为 5 个步骤:定义 → 打开 → 遍历 → 关闭 → 释放。

-- 步骤1:定义游标
DECLARE 游标名称 CURSOR FOR
    查询语句;  -- 游标要遍历的结果集(如SELECT语句)

-- 步骤2:打开游标(初始化结果集)
OPEN 游标名称;

-- 步骤3:遍历游标(逐行处理数据)
-- 3.1 声明变量存储每行数据
DECLARE @变量1 数据类型, @变量2 数据类型;

-- 3.2 读取游标第一行数据到变量
FETCH NEXT FROM 游标名称 INTO @变量1, @变量2;

-- 3.3 循环遍历(@@FETCH_STATUS=0表示读取成功)
WHILE @@FETCH_STATUS = 0
BEGIN
    -- 行级处理逻辑(如插入、更新等)
    操作语句;

    -- 读取下一行数据
    FETCH NEXT FROM 游标名称 INTO @变量1, @变量2;
END;

-- 步骤4:关闭游标(释放结果集资源)
CLOSE 游标名称;

-- 步骤5:释放游标(彻底删除游标对象)
DEALLOCATE 游标名称;

4. 关键语法解释

  • DECLARE 游标名称 CURSOR FOR 查询语句:定义游标并关联要遍历的结果集。
  • OPEN 游标名称:初始化游标,加载结果集到内存。
  • FETCH NEXT FROM 游标名称 INTO @变量:读取游标当前行的数据到变量,并将游标 “移动到下一行”。
  • @@FETCH_STATUS:系统内置变量,返回游标读取状态:
    • 0:读取成功。
    • -1:读取失败(已到结果集末尾)。
    • -2:读取的行不存在。
  • CLOSE 游标名称:关闭游标,释放结果集资源(但游标对象仍存在)。
  • DEALLOCATE 游标名称:彻底删除游标对象,释放所有资源。

(游标遍历数据插入临时表)

-- 1. 先创建临时表(存储游标遍历的结果)
CREATE TABLE #TempTable (
    Score INT,
    ScoreCount INT
);

-- 2. 定义游标:关联“按Score分组统计”的结果集
DECLARE Score_cursor CURSOR FOR
    SELECT Score, COUNT(Score) AS ScoreCount 
    FROM ScoreList
    GROUP BY Score;

-- 3. 打开游标
OPEN Score_cursor;

-- 4. 声明变量存储每行数据
DECLARE @Score INT, @ScoreCount INT;

-- 5. 读取第一行数据
FETCH NEXT FROM Score_cursor INTO @Score, @ScoreCount;

-- 6. 循环遍历游标
WHILE @@FETCH_STATUS = 0
BEGIN
    -- 将当前行数据插入临时表
    INSERT INTO #TempTable(Score, ScoreCount) 
    VALUES (@Score, @ScoreCount);

    -- 读取下一行数据
    FETCH NEXT FROM Score_cursor INTO @Score, @ScoreCount;
END;

-- 7. 关闭游标
CLOSE Score_cursor;

-- 8. 释放游标
DEALLOCATE Score_cursor;

-- 9. 查看临时表数据
SELECT * FROM #TempTable;

二、INSERT INTO 数据插入(从其他表读取数据)

1. 定义

INSERT INTO... SELECT 是 SQL Server 中批量插入数据的语法,直接从一个表(或查询结果)读取数据并插入到目标表中(无需逐行处理)。

2. 用处

  • 批量将一个表的数据(或查询结果)插入到另一个表,效率远高于游标逐行插入。

3. 语法

INSERT INTO 目标表(列1, 列2,...)
SELECT 列1, 列2,...  -- 从源表读取的列(需与目标表列匹配)
FROM 源表
[WHERE 条件];  -- 可选:筛选要插入的数据

(从 ScoreList 插入数据到临时表)

-- 1. 创建临时表
CREATE TABLE #TempTable (
    Score INT,
    ScoreCount INT
);

-- 2. 批量插入:从ScoreList读取“Score在90-100之间”的分组数据
INSERT INTO #TempTable(Score, ScoreCount)
SELECT Score, COUNT(Score) AS ScoreCount 
FROM ScoreList
WHERE Score > 90 AND Score <= 100  -- 筛选条件
GROUP BY Score;

-- 3. 查看临时表数据
SELECT * FROM #TempTable;

游标 vs INSERT INTO 批量插入对比

特性 游标(逐行插入) INSERT INTO... SELECT(批量插入)
操作方式 逐行读取、逐行插入 批量读取、批量插入
效率 低(频繁 IO 操作) 高(一次 IO 操作)
适用场景 复杂行级逻辑(逐行处理) 简单批量数据迁移
代码复杂度 高(需 5 步流程) 低(一行语句)
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐