![](/img/trans.png)
[英]Why is the AND clause in my query being ignored (very simple SQL query)
[英]Why are my SQL indexes being ignored?
我們遇到的問題是,我們的表上的索引被忽略,而SQL Server 2000正在執行表掃描。 我們可以通過使用WITH (INDEX=<index_name>)
子句強制使用索引,但是不希望這樣做。
作為開發人員,我在編寫T-SQL時非常熟悉SQL Server,但是分析和性能調優不是我的強項。 我正在尋找關於為什么會發生這種情況的任何建議和指導。
更新:
我應該說我們已經重建了所有索引並更新了索引統計信息。
其中一個罪魁禍首的表定義如下:
CREATE TABLE [tblinvoices]
(
[CustomerID] [int] NOT NULL,
[InvoiceNo] [int] NOT NULL,
[InvoiceDate] [smalldatetime] NOT NULL,
[InvoiceTotal] [numeric](18, 2) NOT NULL,
[AmountPaid] [numeric](18, 2) NULL
CONSTRAINT [DF_tblinvoices_AmountPaid] DEFAULT (0),
[DateEntered] [smalldatetime] NULL
CONSTRAINT [DF_tblinvoices_DateEntered] DEFAULT (getdate()),
[PaymentRef] [varchar](110),
[PaymentType] [varchar](10),
[SyncStatus] [int] NULL,
[PeriodStart] [smalldatetime] NULL,
[DateIssued] [smalldatetime] NULL
CONSTRAINT [DF_tblinvoices_dateissued] DEFAULT (getdate()),
CONSTRAINT [PK_tblinvoices] PRIMARY KEY NONCLUSTERED
(
[InvoiceNo] ASC
) ON [PRIMARY]
) ON [PRIMARY]
此表上還有另一個索引(我們希望SQL使用的索引):
CustomerID (Non-Unique, Non-Clustered)
以下查詢執行表掃描而不是使用CustomerID
索引:
SELECT
CustomerID,
Sum(InvoiceTotal) AS SumOfInvoiceTotal,
Sum(AmountPaid) AS SumOfAmountPaid
FROM tblInvoices
WHERE CustomerID = 2112
GROUP BY customerID
更新:
在回答Autocracy的問題時 ,這兩個查詢都執行表掃描。
更新:
在回答Quassnoi關於DBCC SHOW_STATISTICS
的問題時 ,數據是:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1667 246 454 8 27.33333
2112 911 3427 16 56.9375
2133 914 775 16 57.125
我們遇到的問題是,我們的表上的索引被忽略,而
SQL Server 2000
正在執行表掃描。
盡管自Aug 29, 1997
年Aug 29, 1997
以來已經過去了4,302
天,但SQL Server
的優化器還沒有演變成SkyNet
,它仍然可以做出一些不正確的決定。
索引提示只是你,一個人,幫助人工智能的方式。
如果您確定收集了統計信息並且優化器仍然存在錯誤,那么請繼續使用提示。
它們是合法的,正確的,文檔化的,並且由Microsoft
支持,以強制執行您想要的查詢計划。
在你的情況下:
SELECT CustomerID,
SUM(InvoiceTotal) AS SumOfInvoiceTotal,
SUM(AmountPaid) AS SumOfAmountPaid
FROM tblInvoices
WHERE CustomerID = 2112
GROUP BY
CustomerID
,優化器有兩個選擇:
KEY LOOKUP
來獲取InvoiceTotal
和AmountPaid
的值 rows fetched per second
行中更快,但在總行數方面更長。 第一種方法可以或可以不比第二種方法快。
優化器嘗試通過查看統計信息來估計哪種方法更快,這使得索引選擇性與其他值保持一致。
對於選擇性索引,前一種方法更快; 對於非選擇性的,后者是。
你可以運行這個查詢:
SELECT 1 - CAST(COUNT(NULLIF(CustomerID, 2112)) AS FLOAT) / COUNT(*)
FROM tlbInvoices
更新:
由於CustomerID = 2112
僅覆蓋了1,4%
的行,因此您應該從使用索引中受益。
現在,您可以運行以下查詢:
DBCC SHOW_STATISTICS ([tblinvoices], [CustomerID])
,在RANGE_HI_KEY
小於2112
,在第三個結果RANGE_HI_KEY
找到兩個鄰接行,並在此處發布行?
更新2:
由於統計數據似乎是正確的,我們只能猜測優化器在這種情況下選擇全表掃描的原因。
可能(可能)這是因為這個非常值( 2112
)發生在RANGE_HI_KEY
並且優化器看到它異常密集(單獨2112
值為3427
,而從1668
到2111
的整個范圍僅為911
)
你能再做兩件事嗎:
運行此查詢:
DBCC SHOW_STATISTICS ([tblinvoices], [CustomerID])
並發布前兩個結果集。
運行此查詢:
SELECT TOP 1 CustomerID,COUNT(*)FROM tblinvoices客戶ID在1668和2111之間
,使用原始查詢中上述查詢中的頂級CustomerID
:
SELECT CustomerID, SUM(InvoiceTotal) AS SumOfInvoiceTotal, SUM(AmountPaid) AS SumOfAmountPaid FROM tblInvoices WHERE CustomerID = @Top_Customer GROUP BY CustomerID
並看看它會產生什么樣的計划。
最好的辦法是通過在CustomerID索引中包含InvoiceTotal和AmountPaid列來使索引成為覆蓋索引。 (在SQL 2005中,您可以將它們添加為“包含”列。在SQL 2000中,您必須將它們添加為其他鍵列。)如果您這樣做,我將保證查詢優化器將選擇您的索引*。
說明 :索引似乎總是有用,但是使用(非覆蓋)索引會產生隱藏成本,這就是必須要執行的“ 書簽查找 ”以檢索可能需要的任何其他列。主表。 此書簽查找是一項昂貴的操作,並且(一種可能)原因是查詢優化器可能不會選擇使用您的索引。
通過在索引本身中包含所有需要的列,完全避免了這個書簽查找,並且優化器不必玩這個小游戲來確定使用索引是否“值得”。
(*)或者我將退還您的StackOverflow積分。 只需發送一個帶有地址的蓋章信封即可...
編輯:是的,如果你的主鍵不是聚集索引,那么一定要做到這一點! 但即使有了這種改變,使您的CustomerID索引成為覆蓋索引也應該將性能提高一個數量級(10倍或更高)!
索引被忽略的最常見原因是
涉及的列不夠有選擇性(優化器決定表掃描會更快,因為'訪問'大量行)
SELECT / GROUP BY / ORDER BY中涉及大量列,並且在使用索引后將涉及查找聚簇索引
統計數據已過期(或因大量插入或刪除而產生偏差)
您是否正在運行常規索引維護作業? (它在Dev環境中缺失很常見)。
Kimberly的最新帖子正是這個話題: http : //www.sqlskills.com/BLOGS/KIMBERLY/post/The-Tipping-Point-Query-Answers.aspx
SQL Server使用基於成本的優化器 ,如果優化器計算查找索引鍵然后查找聚簇索引以檢索其余列的成本高於掃描表的成本,那么它將掃描而是表。 “小費”實際上是驚人的低點。
您是否嘗試將其他列添加到索引中? 即InvoiceTotal和AmountPaid。
這個想法是查詢將被索引“覆蓋”,並且不必返回參考表。
我會開始測試,看看你是否可以將主鍵更改為聚簇索引 。 現在該表被認為是“堆”。 如果你不能這樣做,那么我也會考慮使用聚集索引創建一個視圖,但首先你必須將“AmountPaid”列更改為NOT NULL。 它已經默認為零,所以這可能是一個簡單的改變。 對於視圖我會嘗試類似的東西。
SET QUOTED_IDENTIFIER, ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON
GO
SET NUMERIC_ROUNDABORT OFF
GO
IF EXISTS
(
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = N'CustomerInvoiceSummary'
)
DROP VIEW dbo.CustomerInvoiceSummary
GO
CREATE VIEW dbo.CustomerInvoiceSummary WITH SCHEMABINDING
AS
SELECT a.CustomerID
, Sum(a.InvoiceTotal) AS SumOfInvoiceTotal
, Sum(a.AmountPaid) AS SumOfAmountPaid
, COUNT_BIG(*) AS CT
FROM dbo.tblInvoices a
GROUP BY a.CustomerID
GO
CREATE UNIQUE CLUSTERED INDEX CustomerInvoiceSummary_CLI ON dbo.CustomerInvoiceSummary ( CustomerID )
GO
我想我剛剛發現它。 在我注意到我給你的兩個查詢預計會導致表掃描之前,我正在閱讀發布到你問題的評論,我只想要結果。 也就是說,當有人說你沒有聚集索引時,它引起了我的興趣。 我詳細閱讀了你的SQL create語句,並且很驚訝地注意到了這種情況。 這就是它沒有使用您的CustomerId索引的原因。
您的CustomerId索引引用InvoiceNo的主鍵。 但是,您的主鍵不是群集的,因此您必須查看該索引以查找該行的實際位置。 SQL服務器不會執行兩次非聚集索引查找來查找行。 它只是表掃描。
使您的InvoiceNo成為聚集索引。 我們可以假設它們通常以遞增的方式插入,因此插入成本不會高得多。 但是,您的查詢成本會低得多。 美元到甜甜圈,它會使用你的索引。
編輯:我也喜歡BradC的建議 。 這是一個常見的DBA技巧。 然而,就像他說的那樣,無論如何都要使主要聚集,因為這是問題的原因。 擁有沒有聚簇索引的表是非常罕見的。 大部分時間沒有使用它,這是一個壞主意。 也就是說,他的覆蓋指數是應該完成的群集改進。
其他幾位人士指出,您的數據庫可能需要更新索引統計信息。 您可能在數據庫中擁有如此高的行百分比,以便順序讀取表會比在磁盤中查找每個表更快。 SQL Server有一個花哨的GUI查詢分析器,它會告訴你數據庫認為各種活動的成本是多少。 你可以打開它,看看它到底在想什么。
如果你能給我們,我們可以給你更堅實的答案:
Select * from tblinvoices;
Select * from tblinvoices where CustomerID = 2112;
使用該查詢分析器,並更新您的統計信息。 最后一個提示:你可以使用索引提示強制它使用你的索引,如果你確定它在你完成其他所有事情之后只是愚蠢。
你有沒有嘗試過
exec sp_recompile tblInvoices
...只是為了確保你沒有使用緩存糟糕的計划?
您還可以嘗試對查詢中涉及的表(或表)執行UPDATE STATISTICS。 並不是說我完全理解SQL中的統計信息,但我確實知道這是我們的DBA偶爾會做的事情(每周工作被安排在更大和經常更改的表上更新統計信息)。
嘗試更新統計信息 。 這些統計數據是編譯器決定是否應該使用索引的基礎。 它們包含基數和每個表的行數等信息。
例如,如果自從您進行大量批量導入后沒有更新統計信息,則編譯器可能仍然認為該表中只有10行,而不會打擾索引。
你在使用“SELECT * FROM ...”嗎? 這通常會導致掃描。
我們需要模式,索引和示例查詢來幫助更多
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.