傳統視圖僅是儲存的 `SELECT` 定義 (元資料),每次引用時由優化器展開、重新執行底層聯結/聚合。索引視圖 在視圖上建立唯一叢集索引,將結果集物質化持久化至資料頁,實現「預計算、共享重用」:
| 場景特徵 | 適合度 | 索引視圖設計要點 |
|---|---|---|
| 高頻複雜聚合 (SUM/COUNT/AVG + GROUP BY) | ★★★★★ | 視圖定義包含 GROUP BY、決定性聚合;建立唯一叢集索引於分組鍵 |
| 多表固定模式聯結 (星型/雪花結構) | ★★★★★ | 視圖預聯結事實表與維度表;索引鍵含外鍵欄位 |
| 大表篩選後小結果集 (高選擇性謂詞) | ★★★☆☆ | 視圖含 WHERE 過濾;考慮篩選索引 (2008+) 替代 |
| 頻繁更新的基表 (OLTP 核心表) | ★☆☆☆☆ | 維護成本極高,每筆 DML 觸發視圖索引更新 |
| 非決定性函數 / 外部存取 / 非精確數值 | 不可用 | 視圖定義必須滿足「索引視圖限制」 (見下節) |
視圖定義必須滿足所有條件,才能建立唯一叢集索引:
ANSI_NULLS、QUOTED_IDENTIFIER、CONCAT_NULL_YIELDS_NULL、ARITHABORT、NUMERIC_ROUNDABORT 等選項正確設定UNION、DISTINCT (聚合除外)、TOP、ORDER BYSUM、COUNT_BIG、AVG、MIN、MAX;必須包含 COUNT_BIG(*)GROUP BY 可選;若無則視圖為單一資料列 (標量聚合)SET ANSI_NULLS, QUOTED_IDENTIFIER, CONCAT_NULL_YIELDS_NULL, ARITHABORT ON;
SET NUMERIC_ROUNDABORT OFF;
GO
CREATE VIEW dbo.SalesSummary
WITH SCHEMABINDING
AS
SELECT
ProductID,
OrderDate,
COUNT_BIG(*) AS Cnt,
SUM(Quantity) AS TotalQty,
SUM(UnitPrice * Quantity) AS TotalAmt
FROM dbo.SalesOrderDetail sod
JOIN dbo.SalesOrderHeader soh ON sod.SalesOrderID = soh.SalesOrderID
GROUP BY ProductID, OrderDate;
GO
-- 唯一叢集索引 (鍵含 GROUP BY 欄位)
CREATE UNIQUE CLUSTERED INDEX IDX_SalesSummary_Prod_Date
ON dbo.SalesSummary (ProductID, OrderDate);
GO
-- 可選:非叢集索引支援其他查詢模式
CREATE NONCLUSTERED INDEX IDX_SalesSummary_TotalAmt
ON dbo.SalesSummary (TotalAmt) INCLUDE (TotalQty);
OPTION (EXPAND VIEWS) 強制展開 (不使用視圖索引)WITH (NOEXPAND) 提示,否則優化器仍會展開視圖定義SELECT * FROM dbo.SalesSummary WITH (NOEXPAND) WHERE ProductID = 100;唯一叢集索引鍵通常對應 `GROUP BY` 欄位。當查詢含非鍵欄位篩選/排序時,需輔助索引:
| 查詢模式 | 無索引視圖 (ms) | 有索引視圖 (ms) | 提升倍數 |
|---|---|---|---|
| 單日產品銷售彙總 (GROUP BY ProductID, OrderDate) | 12,450 | 18 | 692x |
| 前 10 大銷售額產品 (ORDER BY TotalAmt DESC) | 9,800 | 5 | 1,960x |
| 特定產品年度趨勢 (WHERE ProductID=X GROUP BY MONTH) | 3,200 | 12 | 267x |
測試環境:SQL Server 2005 SP4, 8-core, 32GB RAM, 100萬筆 SalesOrderDetail, 3萬筆 SalesOrderHeader
-- 檢視視圖索引使用統計
SELECT
OBJECT_NAME(s.object_id) AS ViewName,
i.name AS IndexName,
s.user_seeks, s.user_scans, s.user_lookups,
s.last_user_seek, s.last_user_scan
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE OBJECTPROPERTY(s.object_id, 'IsIndexedView') = 1;
-- 視圖定義與選項檢查
SELECT
OBJECT_NAME(object_id) AS ViewName,
uses_ansi_nulls, uses_quoted_identifier,
is_schema_bound, has_unchecked_assembly_data
FROM sys.views
WHERE OBJECTPROPERTY(object_id, 'IsIndexedView') = 1;
下篇將進階探討:分區視圖與索引視圖結合、參數化查詢下的匹配失效分析、資料倉庫 ETL 階段利用索引視圖加速彙總、以及遷移至 SQL Server 2012+ 後的柱存索引替代方案對比。