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

資料庫效能調優實戰:索引設計、連接條件與 WHERE 子句三大核心優化策略

     編輯:關於SqlServer

不良 SQL 效能多源於三大根因:索引設計不當、連接條件不足、WHERE 子句無法利用索引。本文結合實測數據(Sybase 11.0.3,62 萬筆測試表),深度剖析三大優化方向與具體寫法。

一、索引設計:群集 vs 非群集 vs 組合索引

測試表 `record`(62 萬筆),查詢條件含 `date`(大量重複值)、`place`、`amount`。

索引類型查詢 1:date 範圍 + amount查詢 2:date GROUP BY查詢 3:date 範圍 + place IN
date 非群集25 秒55 秒27 秒
date 群集14 秒28 秒14 秒
組合 (place, date, amount)26 秒27 秒<1 秒(索引覆蓋)
組合 (date, place, amount)<1 秒(覆蓋)11 秒<1 秒(覆蓋)

核心結論

  • 群集索引:適合大量重複值、頻繁範圍查詢(BETWEEN, >, <)、ORDER BY/GROUP BY 的欄位。資料物理順序與索引一致,範圍掃描極快。
  • 組合索引:前導欄位必須為最高頻率查詢欄位;盡量讓關鍵查詢形成索引覆蓋(所有查詢欄位皆在索引中),避免回表。
  • 預設建立的非群集索引未必最佳,需依查詢模式分析調整。

二、連接條件:充分條件才能誘導最佳執行計畫

測試表 `card`(7,896 筆,card_no 非群集)與 `account`(191,122 筆,account_no 非群集)。

連接條件執行時間I/O 估算最佳驅動表
`a.card_no = b.card_no`20 秒595,907account (外層) → card (內層)
`a.card_no = b.card_no AND a.account_no = b.account_no`<1 秒33,528card (外層) → account (內層)

原則

  1. 連接條件需包含帶索引的欄位,且優先考慮行數較少的表做外層驅動表。
  2. I/O 成本公式:`外層表頁數 + (外層表行數 × 內層表單次查找頁數)`,乘積最小者為最佳計畫。
  3. 使用 `SET SHOWPLAN ON` 檢視執行計畫;`DBCC TRACEON(3604,310,302)` 取得詳細資訊(需 SA 權限)。

三、WHERE 子句:避免運算導致索引失效

1. 欄位運算移至等號右側

低效寫法(掃表)高效寫法(用索引)原理
`SUBSTRING(card_no,1,4)='5378'` (13秒)`card_no LIKE '5378%'` (<1秒)函數/運算在執行時逐列計算,無法用索引
`amount/30 < 1000` (11秒)`amount < 1000*30` (<1秒)同左
`CONVERT(char(10),date,112)='19991201'` (10秒)`date = '1999/12/01'` (<1秒)同左

2. IN / OR 易觸發工作表策略,導致索引失效

測試 `stuff` 表(20 萬筆,id_no 非群集):

  • `WHERE id_no IN ('0','1')` → 23 秒(轉為 OR 策略,建立臨時工作表,未用索引)
  • 拆分為兩條 `UNION ALL` 或分別查詢再相加 → 3 秒(各用索引)
  • 或寫存儲過程分別查詢加總 → 同樣快

建議:不產生大量重複值時,拆分 IN/OR;需去重時用 `UNION`(非 `UNION ALL`)。

3. 善用存儲過程封裝邏輯,減少網路往返與編譯開銷

開發規範速查(節錄)

SQL 寫法

  • 避免大事務、慎用 `HOLDLOCK`,提升並發。
  • 避免重複存取大表,先放入暫存表再連接。
  • 慎用游標,超過 1 萬行必改寫;游標中避免再做連接。
  • WHERE 條件順序依索引順序、範圍大小排序(大→小)。
  • **絕不**在 `=` 左側對欄位做函數/運算。
  • 用 `EXISTS` 代替 `COUNT(1)` 判斷存在性;`COUNT(1)` 優於 `COUNT(*)`。
  • 優先用 `>=` 而非 `>`。
  • 注意 `OR` 與 `UNION` 互換場景。
  • 連接欄位資料型別必須一致,避免隱含轉換。
  • 單次 INSERT/UPDATE 超過 200 頁(400KB)會觸發鎖升級(頁鎖→表鎖)。

索引使用

  • 大型 OLTP 表建議不超過 6 個索引。
  • 優先用索引欄位查詢,必要時 `INDEX (index_name)` 強制指定。
  • 避免大表 Table Scan,必要時新增索引。
  • 聯合索引必須用到第一欄位才會被使用。
  • 定期重建索引、重新編譯存儲過程。

TempDB 使用

  • 減少 `DISTINCT/ORDER BY/GROUP BY/HAVING/JOIN/COMPUTE` 等加重 TempDB 負擔的操作。
  • 避免頻繁建刪暫存表;大量資料用 `SELECT INTO` 避免 Log,小量用 `CREATE TABLE + INSERT`。
  • 大暫存表需建索引時,將建表與建索引放入子存儲過程,確保優化器能用到索引。
  • 結束時顯式 `TRUNCATE TABLE` 再 `DROP TABLE`,減少系統表鎖定時間。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved