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

PHP + MSSQL 分頁查詢完整實作:兩種高效寫法與通用分頁類

     編輯:PHP 綜合知識

SQL Server 無 LIMIT 子句,傳統分頁需用 TOP + NOT IN 或 ROW_NUMBER() 窗口函數。本文提供兩種經過實測的分頁寫法、完整可執行範例程式碼,並封裝為通用分頁工具類,支援自定義每頁筆數、頁碼傳遞、上一頁/下一頁/首尾頁導航。

寫法一:TOP + NOT IN(適合舊版 SQL Server、資料量中等)

$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$pageSize = 30;
$offset = ($page - 1) * $pageSize;

// 總筆數
$total = (int) mssql_result(mssql_query("SELECT COUNT(1) FROM reguser"), 0);
$pageCount = ceil($total / $pageSize);

// 分頁查詢
$sql = "SELECT TOP $pageSize * FROM reguser
        WHERE id NOT IN (
            SELECT TOP $offset id FROM reguser ORDER BY id DESC
        )
        ORDER BY id DESC";

原理:子查詢先取出前 $offset 筆 ID,主查詢排除這些 ID 再取 TOP $pageSize。缺點:子查詢隨頁碼增大效能下降,不適合百萬級資料深分頁。

寫法二:ROW_NUMBER() 窗口函數(SQL Server 2005+、大資料首選)

$sql = "SELECT * FROM (
            SELECT ROW_NUMBER() OVER (ORDER BY id DESC) AS rn, *
            FROM reguser
        ) AS t
        WHERE rn BETWEEN " . ($offset + 1) . " AND " . ($offset + $pageSize);

優勢:單次掃描、索引友善、深分頁效能穩定。若需相容 SQL Server 2000,可改用 IDENTITY 暫存表法。

完整可執行範例(含導航列)


" . mssql_field_name($rs, $i) . ""; ?>
$val"; ?>
1) echo "首頁 上一頁 "; if ($page < $maxPage) echo "下一頁 尾頁"; ?>

通用分頁工具類(PDO 版,支援 MSSQL/MySQL/PostgreSQL)

class Paginator {
    private $pdo;
    private $pageSize;
    private $totalRows;
    private $currentPage;
    private $maxPage;

    public function __construct(PDO $pdo, $pageSize = 20) {
        $this->pdo = $pdo;
        $this->pageSize = $pageSize;
    }

    public function setTotalRows($countSql, $params = []) {
        $stmt = $this->pdo->prepare($countSql);
        $stmt->execute($params);
        $this->totalRows = (int) $stmt->fetchColumn();
        $this->maxPage = max(1, (int) ceil($this->totalRows / $this->pageSize));
        return $this;
    }

    public function setCurrentPage($page) {
        $this->currentPage = max(1, min((int)$page, $this->maxPage));
        return $this;
    }

    public function fetchPage($dataSql, $params = []) {
        $offset = ($this->currentPage - 1) * $this->pageSize;
        // 不同資料庫分頁語法不同,這裡以 MSSQL ROW_NUMBER 為例
        $pagedSql = "SELECT * FROM ($dataSql) AS t WHERE rn BETWEEN " . ($offset + 1) . " AND " . ($offset + $this->pageSize);
        $stmt = $this->pdo->prepare($pagedSql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getNavHtml($baseUrl = '?') {
        $html = "第 {$this->currentPage}/{$this->maxPage} 頁,共 {$this->totalRows} 筆 ";
        if ($this->currentPage > 1) $html .= "首頁 上一頁 ";
        if ($this->currentPage < $this->maxPage) $html .= "下一頁 尾頁";
        return $html;
    }
}

使用通用類別範例

$pdo = new PDO("sqlsrv:Server=MYSQL2005;Database=edu_dzk", 'sa', '123456');
$pager = new Paginator($pdo, 10);
$pager->setTotalRows("SELECT COUNT(1) FROM reguser")
      ->setCurrentPage($_GET['page'] ?? 1);
$data = $pager->fetchPage("
    SELECT ROW_NUMBER() OVER (ORDER BY id DESC) AS rn, *
    FROM reguser
");
echo $pager->getNavHtml();

效能優化建議

  1. 索引覆蓋:ORDER BY 欄位加索引,避免 Sort 運算子。
  2. 僅選需要欄位:避免 SELECT *,減少 I/O 與網路傳輸。
  3. 游標分頁(Keyset Pagination):極大資料集(>1000萬)改用 WHERE id > $lastId ORDER BY id LIMIT $pageSize,效能 O(1)。
  4. 快取總筆數:總筆數變動低頻時,用 Redis 快取 COUNT(*) 結果。

總結

MSSQL 分頁核心在於 ROW_NUMBER() OVER (ORDER BY ...) 窗口函數,配合 BETWEEN 取區間。封裝通用 Paginator 類別可屏蔽資料庫差異,專案中統一呼叫,提升開發效率與程式碼可維護性。

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