在 B/S 架構專案中,高併發與海量資料是常態。某專案中單表記錄達 9 位數(訂單表 8 位數),查詢需求為「依代理商查詢某期間所有會員卡產生的訂單」。兩表結構如下:
cardNo(會員卡號)、orderTime(訂單時間)等欄位cardNo、proxyID(代理商編號)等欄位原始 SQL 採用 INNER JOIN 並以 IN 子句篩選代理商:
SELECT 欄位 FROM [Order]
INNER JOIN [Member] ON [Order].cardNo = [Member].cardNo
WHERE [Member].proxyID IN ('A-01', 'A-02')
AND [Order].orderTime BETWEEN '20080101' AND '20080131';
當兩表皆超過百萬筆且欄位眾多時,即便有索引支援,查詢仍可能陷入長時間等待。
核心思路:輸出欄位多來自 Order 表,Member 表僅提供「代理商→會員卡號」的映射關係。先將符合條件的 cardNo 取出存入表變量,再與 Order 表關聯。
DECLARE @t TABLE (cardNo INT);
INSERT @t
SELECT cardNo FROM [Member] WHERE proxyID IN ('A-01', 'A-02');
SELECT 欄位 FROM [Order]
INNER JOIN @t ON [Order].cardNo = @t.cardNo
WHERE [Order].orderTime BETWEEN '20080101' AND '20080131';
優勢:
DROP,減輕 tempdb 壓力。注意:表變量無統計資訊,若卡號筆數極大(超過萬筆),優化器可能誤判行數,建議改用暫存表 #t 並加上 CREATE INDEX。
核心思路:Member 表僅需 cardNo、proxyID 兩欄位參與查詢。建立僅含這兩欄的視圖並加上唯一叢集索引,使其物化儲存,查詢時直接掃描視圖而非基底表。
CREATE VIEW dbo.MemberView
WITH SCHEMABINDING
AS
SELECT cardNo, proxyID FROM dbo.[Member];
GO
CREATE UNIQUE CLUSTERED INDEX IX_MemberView_cardNo
ON dbo.MemberView (cardNo);
GO
查詢改為:
SELECT 欄位 FROM [Order]
INNER JOIN dbo.MemberView ON [Order].cardNo = MemberView.cardNo
WHERE MemberView.proxyID IN ('A-01', 'A-02')
AND [Order].orderTime BETWEEN '20080101' AND '20080131';
WITH SCHEMABINDING,且引用物件需雙部分命名(schema.table)。UNIQUE CLUSTERED,才能保證行唯一性並物化儲存。INSERT/UPDATE/DELETE 會同步維護視圖索引,寫入效能會下降,需評估讀寫比例。OUTER JOIN、UNION、子查詢等複雜邏輯。| 維度 | 表變量 | 索引視圖 |
|---|---|---|
| 適用場景 | 臨時、參數化、單次查詢 | 固定查詢模式、高頻讀取、低頻寫入 |
| 開發成本 | 低,僅修改查詢邏輯 | 中,需建立視圖、索引、權限 |
| 維護成本 | 無額外維護 | 寫入效能下降、結構變更需重建視圖 |
| 統計資訊 | 無(可能誤判) | 完整統計資訊,優化器決策更準確 |
建議:優先評估索引視圖,若寫入頻繁或查詢條件多變,改用表變量或暫存表。實際專案中可結合 SET STATISTICS IO/TIME ON 實測比對。