首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 手寫分頁函數:Access/OleDb 通用分頁邏輯與 SQL Server 現代化對比

     編輯:關於C語言

前言

早期 ASP.NET 連接 Access (`.mdb`) 使用 `OleDb`,不支援 `OFFSET/FETCH`,需自行構造分頁 SQL。本文分析原始碼邏輯,指出效能與正確性問題,並提供 SQL Server 現代分頁寫法對比。

原始碼邏輯分析


// 核心邏輯:利用 TOP + 子查詢排除前 N 筆
// 第 1 頁
SELECT TOP @PageSize * FROM Table WHERE [Condition] ORDER BY @OrderField @OrderDir

// 第 N 頁 (N>1)
SELECT TOP @PageSize * FROM Table 
WHERE @OrderField > ALL (
    SELECT TOP @Offset @OrderField FROM Table WHERE [Condition] ORDER BY @OrderField @OrderDir
) ORDER BY @OrderField @OrderDir

關鍵缺陷

  1. 排序欄位必須唯一:`>` 比較若遇重複值,會漏掉/重複資料。需改用 `>=` + `TOP` 配合唯一鍵 (如 `ID`)。
  2. 子查詢效能差:`> ALL (SELECT TOP N ...)` 會掃描大量資料,隨頁碼增大線性惡化。
  3. 字串拼接 SQL 注入風險:`field`、`tablename`、`condition` 直接拼接。
  4. 不支援複雜條件:`condition` 字串處理脆弱。
  5. Access `TOP` 不支援參數化:`TOP @PageSize` 在 OleDb 中常失效,需字串拼接。

改良版 Access 分頁 (參數化 + 唯一鍵)


public DataTable GetPageAccess(string table, string[] fields, string where, string orderBy, int pageSize, int pageIndex)
{
    // 1. 總筆數
    string countSql = $"SELECT COUNT(*) FROM {table} WHERE {where}";
    int total = (int)ExecuteScalar(countSql, GetParams(where));

    // 2. 分頁查詢 (使用 ID 唯一鍵)
    string fieldList = string.Join(",", fields);
    string sql;
    var parameters = GetParams(where); // 解析 where 中的參數

    if (pageIndex == 1)
    {
        sql = $"SELECT TOP {pageSize} {fieldList} FROM {table} WHERE {where} ORDER BY {orderBy}";
    }
    else
    {
        int offset = (pageIndex - 1) * pageSize;
        // 子查詢取得第 offset 筆的 ID
        sql = $@"
            SELECT TOP {pageSize} {fieldList} FROM {table} 
            WHERE {where} AND ID > (
                SELECT MAX(ID) FROM (
                    SELECT TOP {offset} ID FROM {table} WHERE {where} ORDER BY {orderBy}
                ) AS T
            ) ORDER BY {orderBy}";
    }
    return ExecuteDataTable(sql, parameters);
}

SQL Server 現代分頁 (2012+)


string sql = @"
    SELECT Id, Title, Content, CreateTime
    FROM Articles
    WHERE IsPublished = 1
    ORDER BY CreateTime DESC
    OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";

var parameters = new { Offset = (page - 1) * pageSize, PageSize = pageSize };
var items = conn.Query
(sql, parameters).ToList(); int total = conn.ExecuteScalar("SELECT COUNT(*) FROM Articles WHERE IsPublished=1");

大數據量高效分頁策略對比

策略適用場景優點缺點
OFFSET/FETCH中小資料量、任意頁碼跳轉標準語法、支援任意排序深分頁 (第 10萬頁) 掃描前 N 行,慢
游標分頁無限滾動、API 分頁、大資料極快、穩定、不隨頁碼劣化無法隨意跳頁、需唯一排序鍵
覆蓋索引所有分頁查詢避免回表、極大提升速度寫入略慢、佔空間
快取總數總數變化低頻省去 COUNT(*)需快取失效機制

游標分頁範例


// 第一頁
SELECT TOP 20 * FROM Articles WHERE IsPublished=1 ORDER BY Id DESC

// 後續頁 (客戶端傳回上一頁最後一筆 ID)
SELECT TOP 20 * FROM Articles WHERE IsPublished=1 AND Id < @LastId ORDER BY Id DESC

ORM 統一分頁介面


public interface IPagedRepository
{
    Task> GetPagedAsync(int page, int pageSize, Expression> filter, 
        Expression> orderBy, bool descending = true);
}

public class PagedResult
{
    public List Items { get; set; }
    public int TotalCount { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
    public int TotalPages => (TotalCount + PageSize - 1) / PageSize;
}

總結

手寫分頁核心是 **`COUNT(*)` + `TOP`/`OFFSET` 子查詢**。Access 受限於引擎,需自行構造子查詢並注意唯一鍵與參數化。SQL Server 2012+ 直接用 `OFFSET/FETCH`,大數據改用 **游標分頁**。現代開發**封裝 Repository 介面**,上層呼叫 `GetPagedAsync`,底層根據資料庫切換實作,業務邏輯完全隔離。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved