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

SQL 優化實戰案例:虛擬索引驗證與 IN 列表迭代器將 Cost 從 151K 降至 207

     編輯:關於SqlServer

案例背景

  • 環境:Oracle 資料庫 (原文為 Oracle,原理同樣適用 SQL Server)。
  • 問題 SQL:雙表聯結 (`BILL_GRANT_CHECK` 732 萬列 + `BILL_CLASS` 206 列),`WHERE` 含 `IN` 列表 80+ 常數值。
  • 原執行計畫:`HASH JOIN` + 兩表 `FULL TABLE SCAN`,Cost **151,000**,預估 30 分鐘。
  • 關鍵觀察:結果集 732 萬 / 4.53 億 ≈ 1.6%,極適合索引存取;`IN` 列表為常數,可觸發 **INLIST ITERATOR**。

優化步驟

  1. 建立虛擬索引 (無段索引) 快速驗證效果,不影響生產:
    `CREATE INDEX billgrant_class ON bill_grant_check(bill_class) NOSEGMENT;`
    `ALTER SESSION SET "_use_nosegment_indexes"=TRUE;`
  2. 加入 Hint 強制使用索引並觀察新計畫:
    `EXPLAIN PLAN FOR SELECT /*+ INDEX(a billgrant_class) */ ...`
  3. 新計畫結果:
    `MERGE JOIN` → `INLIST ITERATOR` → `TABLE ACCESS BY INDEX ROWID` (BILL_GRANT_CHECK) → `INDEX RANGE SCAN` (虛擬索引)
    Cost 從 **151,000 降至 207**,提升 **約 730 倍**。
  4. 提交真實索引建立申請,上線後持續監控統計資料準確性。

核心知識點

概念說明
虛擬索引Oracle 特有 `NOSEGMENT` 索引,僅存在資料字典,無實體段,配合隱藏參數 `_use_nosegment_indexes` 讓優化器「看到」索引但不實際存取,極適合上線前驗證。
INLIST ITERATOR針對 `IN (const1, const2, ...)` 將列表展開為多次 `INDEX RANGE SCAN` 並合併結果,避免全表掃描。SQL Server 同樣支援 (顯示為 `Index Seek` 多次或 `Constant Scan` + `Nested Loops`)。
選擇性結果集佔表總列數比例。通常 < 5-10% 考慮索引;但需結合聚簇/非聚簇、書籤查找成本綜合判斷。
統計資料準確性Cost 僅在統計資料準確時具參考價值。定期 `UPDATE STATISTICS` / `DBMS_STATS.GATHER_TABLE_STATS` 至關重要。

SQL Server 對應實作

-- 1. 建立真實索引 (線上建立避免鎖表)
CREATE NONCLUSTERED INDEX IX_BillGrantCheck_BillClass 
ON dbo.Bill_Grant_Check (Bill_Class) 
WITH (ONLINE = ON, DATA_COMPRESSION = PAGE);

-- 2. 更新統計資料
UPDATE STATISTICS dbo.Bill_Grant_Check IX_BillGrantCheck_BillClass WITH FULLSCAN;

-- 3. 驗證計畫 (清除快取後)
DBCC FREEPROCCACHE; -- 謹慎使用
SET STATISTICS IO, TIME ON;
SELECT ... FROM ... WHERE Bill_Class IN (...); -- 觀察 Index Seek + IN 列表常數掃描

避坑指南

  • **過度索引**:寫入密集表每新增一索引增加 INSERT/UPDATE/DELETE 開銷,權衡讀寫比例。
  • **參數嗅探**:若 `IN` 列表來自參數化查詢 (`@list`),優化器可能無法展開為常數,導致計畫不穩。考慮 `OPTION (RECOMPILE)` 或動態 SQL。
  • **隱含轉換**:`Bill_Class` 為 `VARCHAR` 而列表傳入數值常數,會導致 `CONVERT_IMPLICIT` 使索引失效。確保資料類型一致。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved