執行 SELECT COUNT(*) FROM Sales.SalesOrderDetail (121,317 列) 時,執行計畫顯示:
原因:SQL Server 必須實際掃描所有列以確保計數準確 (考慮交易隔離、未提交資料、幽靈記錄等),無法直接從中繼資料取得。
SELECT
OBJECT_NAME(p.object_id) AS TableName,
SUM(p.rows) AS RowCount
FROM sys.partitions p
WHERE p.object_id = OBJECT_ID('Sales.SalesOrderDetail')
AND p.index_id IN (0, 1); -- 0=Heap, 1=Clustered Index
GROUP BY p.object_id;
sys.partitions.rows 中繼資料,非交易一致,可能包含未提交/已刪除未清理列。-- 1. 建立計數表
CREATE TABLE dbo.TableRowCounts (
SchemaName SYSNAME NOT NULL,
TableName SYSNAME NOT NULL,
RowCount BIGINT NOT NULL DEFAULT 0,
LastUpdated DATETIME NOT NULL DEFAULT GETDATE(),
PRIMARY KEY (SchemaName, TableName)
);
-- 2. 建立 AFTER INSERT/DELETE 觸發器 (範例: SalesOrderDetail)
CREATE TRIGGER trg_SalesOrderDetail_Count
ON Sales.SalesOrderDetail
AFTER INSERT, DELETE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @delta INT = (SELECT COUNT(*) FROM inserted) - (SELECT COUNT(*) FROM deleted);
IF @delta <> 0
BEGIN
UPDATE dbo.TableRowCounts
SET RowCount = RowCount + @delta,
LastUpdated = GETDATE()
WHERE SchemaName = 'Sales' AND TableName = 'SalesOrderDetail';
IF @@ROWCOUNT = 0
INSERT dbo.TableRowCounts (SchemaName, TableName, RowCount)
VALUES ('Sales', 'SalesOrderDetail', @delta);
END
END;
-- 1. 建立 SCHEMABINDING 檢視
CREATE VIEW dbo.vw_SalesOrderDetail_Count
WITH SCHEMABINDING
AS
SELECT COUNT_BIG(*) AS RowCount
FROM Sales.SalesOrderDetail;
-- 2. 建立唯一叢集索引使其物質化
CREATE UNIQUE CLUSTERED INDEX IX_vw_SalesOrderDetail_Count
ON dbo.vw_SalesOrderDetail_Count (RowCount); -- 單一列索引
CREATE CLUSTERED COLUMNSTORE INDEX CCI_SalesOrderDetail
ON Sales.SalesOrderDetail;
-- COUNT(*) 透過 Segment 消除 + 字典編碼,極速完成
適用於唯讀/少寫入的大型事實表,壓縮率 10x,掃描速度 100x。
| 場景 | 推薦方案 | 精確度 | 寫入影響 | 版本需求 |
|---|---|---|---|---|
| 監控/估算/容量規劃 | sys.partitions | 近似 (±5-10%) | 無 | 所有版本 |
| OLTP 核心業務表、精確計數、中等寫入 | 索引檢視 | 精確 (交易一致) | 低 (內部維護) | 2005+ |
| 極高寫入 OLTP、需自定義邏輯 (如軟刪除不計數) | 觸發器 + 計數表 | 精確 (可控) | 中 (Trigger 開銷) | 所有版本 |
| 資料倉儲事實表、唯讀/批次載入 | Columnstore 索引 | 精確 | 載入時重建 | 2012+ |
| 含檔資料庫 / 跨實例遷移 | sys.partitions 或應用層快取 | 視策略 | 無 | 2012+ |
大表 COUNT(*) 效能問題本質是「精確一致性」與「掃描成本」的矛盾。選擇策略核心在於:**業務是否容忍近似值?寫入頻率如何?版本支援何種特性?** 現代架構傾向於 **索引檢視** (OLTP) 或 **Columnstore** (DW) 實現免維護精確計數,配合應用層快取 (Redis) 進一步卸載資料庫讀取壓力。