Sql-Server

哪些索引可以幫助提高使用 nvarchar 欄位上的 where 子句進行多連接查詢的速度?

  • February 18, 2022

我有一個多連接 SQL 查詢。

SELECT A.COMPANYEMAIL, A.CONTRACTNAME, 
B.CLAIMNAME, C.PARTNAME, C.PARTPRICE, 
C.DAMAGEAMOUNT, C.DAMAGEDATE
FROM CONTRACT A
JOIN CLAIM B ON A.ID=B.CONTRACTID
JOIN PART C ON B.ID=C.CLAIMID
WHERE A.COMPANYEMAIL='XYZ@ABC.COM'
ORDER BY DAMAGEDATE DESC

每個表中的 ID 列是主鍵,聚群。每個表至少有 10M 條記錄。

這樣做是否有意義:

  1. 在 B.CONTRACTID 和 C.CLAIMID 上創建非聚集索引
  2. 代替非聚集索引,創建非聚集覆蓋索引,例如 B.CONTRACTID (INCLUDE B.CLAIMNAME) 和 C.CLAIMID (INCLUDE PARTNAME)?
  3. A.EMAIL 列具有唯一索引 - 可以做些什麼來改進 WHERE 子句並防止它必須讀取表的所有頁面?如果我在 A.EMAIL 上添加非聚集索引,那會有幫助嗎?或者我還需要包括 A.ID 和 A.CONTRACTNAME 嗎?
  4. 請問還有其他索引推薦嗎?
  1. 在 B.CONTRACTID 和 C.CLAIMID 上創建非聚集索引

不,它們本身是非覆蓋的,並且會讓您在大型表上進行 Key Lookup 操作。Key Lookup 是基於行的操作,這意味著如果您輸入 1000 萬行,它將轉身並執行 1000 萬個獨立的 Key Lookup。

  1. 代替非聚集索引,創建非聚集覆蓋索引,例如 B.CONTRACTID (INCLUDE B.CLAIMNAME) 和 C.CLAIMID (INCLUDE PARTNAME)?

是/否

  1. 在這種情況下,CLAIM 上的索引會很好,因為您建議的索引在 where 子句中使用的列上有一個鍵,並且在 select 語句中返回的列上有一個 INCLUDE。
  2. 但是,您在 PART 上提出的索引只有 CLAIMID 上的鍵和 PARTNAME 上的 INCLUDE。這仍然使 PARTPRICE、DAMAGEAMOUNT 和 DAMAGEDATE 未被發現,需要通過鍵查找來找到它們的值。當然,這就是您正在排序的所有事實,這將對鍵和索引的包含產生重大影響。稍後再談。

3 A.EMAIL 列具有唯一索引。可以做些什麼來改進 WHERE 子句並防止它必須讀取表的所有頁面?如果我在 A.EMAIL 上添加非聚集索引,那會有幫助嗎?或者我還需要包括 A.ID 和 A.CONTRACTNAME 嗎?

是的,您會想嘗試在 CONTACT 上添加一個非集群索引,並且您希望 INCLUDE 用於 CONTRACTNAME。不需要包含 ID,因為它隱式包含在所有 NONCLUSTERED 索引中,因為它是 CLUSTERED 鍵。您可能希望將其視為 INCLUDE 的唯一時間是表上的 CLUSTERED 索引是否有可能在將來發生更改。

4.請問還有其他索引推薦嗎?

一個 ORDER BY 可能會給這裡提到的每一件事帶來一個猴子皺紋。由於您要連接三個表,因此您基本上需要在 JOIN 操作的性能或數據排序時的性能之間做出決定。您說每個表中有 10M 行。由於您只有 WHERE 子句在 EMAIL 上,因此我懷疑您將提前大幅縮小結果範圍,從而使 ORDER By 操作更簡單。例如,如果查詢返回 5 到 10 行,那還不足以擔心優化。但是,如果您要返回 100 萬行,那麼您需要重新考慮您的策略。

總而言之,我將創建以下三個索引作為起點,並對其進行測試。與大多數索引操作一樣,沒有要測試的完整數據集、查詢計劃和 I/O 統計資訊,我們只能猜測這些對於您的情況是最佳的。現在,我什至不會擔心 ORDER BY 而不知道這個查詢平均返回多少行。

CREATE NONCLUSTERED INDEX IX_Contract_CompanyEmail
   ON Contract(CompanyEmail) INCLUDE(ContractName);

CREATE NONCLUSTERED INDEX IX_Claim_ContractID
   ON Claim(ContractID) INCLUDE(ClaimName);

CREATE NONCLUSTERED INDEX IX_Part_ClaimId
   ON Part(ClaimId) INCLUDE(PartName,PartPrice, DamageAmount,DamageDate);

引用自:https://dba.stackexchange.com/questions/307657