2009-07-09 30 views
6

Tôi có một câu hỏi khá đơn giản:SQL Server sẽ không sử dụng chỉ mục của tôi

SELECT 
    col1, 
    col2… 
FROM 
    dbo.My_Table 
WHERE 
    col1 = @col1 AND 
    col2 = @col2 AND 
    col3 <= @col3 

Nó được thực hiện khủng khiếp, vì vậy tôi đã thêm một chỉ mục trên col1, col2, col3 (int, bit, và datetime). Khi tôi kiểm tra kế hoạch truy vấn, nó đã bỏ qua chỉ mục của tôi. Tôi đã thử sắp xếp lại các cột trong chỉ mục trong mọi cấu hình có thể và nó luôn bỏ qua chỉ mục. Khi tôi chạy truy vấn nó thực hiện quét chỉ mục nhóm (kích thước bảng là từ 700K đến 800K hàng) và mất 10-12 giây. Khi tôi buộc nó sử dụng chỉ mục của tôi, nó sẽ trả về ngay lập tức. Tôi đã cẩn thận để xóa bộ nhớ đệm và bộ đệm giữa các thử nghiệm.

Những điều khác tôi đã cố gắng:

UPDATE STATISTICS dbo.My_Table 

CREATE STATISTICS tmp_stats ON dbo.My_Table (col1, col2, col3) WITH FULLSCAN 

Tôi có thiếu bất cứ điều gì ở đây? Tôi ghét đặt một gợi ý chỉ mục trong một thủ tục được lưu trữ, nhưng SQL Server dường như không thể có được một đầu mối về điều này. Bất cứ ai biết bất kỳ những thứ khác mà có thể ngăn chặn SQL Server từ nhận ra rằng bằng cách sử dụng chỉ mục là một ý tưởng tốt?

EDIT: Một trong những cột được trả lại là một cột TEXT, vì vậy sử dụng một chỉ số bao phủ hoặc một BAO GỒM sẽ không hoạt động :(

+1

Bạn đặt dấu ba chấm trong danh sách cột, bạn đang chọn cột nào khác? Điều gì xảy ra nếu nó chỉ là col1, col2 và col3? –

+0

Chris, điểm tốt nhưng "Khi tôi buộc nó sử dụng chỉ mục của tôi nó trả về ngay lập tức" bao gồm đó. –

+0

Dấu ba chấm là do truy vấn trả về tất cả các cột trong bảng. Tôi cho rằng tôi có thể đặt SELECT * –

Trả lời

13

Bạn có 800k hàng được lập chỉ mục bởi col1, col2, col3. Col2 là một chút, do đó, khả năng chọn lọc của nó là 50%. Col3 được kiểm tra trên một phạm vi (< =), do đó, khả năng chọn lọc của nó sẽ xấp xỉ khoảng 50%. Mà lá col1. Truy vấn được biên dịch cho kế hoạch chung, được parametrized, vì vậy nó phải tính đến trường hợp chung. Nếu bạn có 10 giá trị khác nhau của col1, thì chỉ mục của bạn sẽ trả lại khoảng 800k/10 * 25% đó là khoảng 20k phím để tra cứu trong chỉ mục nhóm để truy xuất phần '...'. Nếu bạn có 10k giá trị col1 riêng biệt thì chỉ mục sẽ trả về chỉ 20 phím để tra cứu. Như bạn có thể thấy, điều quan trọng không phải là cách bạn xây dựng chỉ mục trong trường hợp này, mà là dữ liệu thực tế. Dựa trên tính chọn lọc của col1, trình tối ưu hóa sẽ chọn một kế hoạch dựa trên quét chỉ mục nhóm (tốt hơn 20k tra cứu chính, mỗi lần tra cứu với chi phí ít nhất 3-5 lần đọc) hoặc một dựa trên không chỉ số nhóm (nếu col1 đủ chọn lọc). Trong thực tế, sự phân bố của col1 cũng đóng một vai trò, nhưng đi vào đó sẽ làm phức tạp quá trình giải thích quá nhiều.

Bạn có thể đi kèm với lợi ích của việc nhận thức và yêu cầu kế hoạch sai, nhưng kế hoạch là ước tính chi phí tốt nhất dựa trên dữ liệu có sẵn tại thời gian biên dịch. Bạn có thể ảnh hưởng đến các gợi ý (gợi ý chỉ mục như bạn gợi ý hoặc tối ưu hóa các gợi ý như Quassnoi gợi ý) nhưng sau đó truy vấn của bạn có thể hoạt động tốt hơn cho bộ thử nghiệm của bạn và tệ hơn nhiều cho một tập hợp dữ liệu khác, ví dụ như khi @ col1 = <the value that matches 500k records>. Bạn cũng có thể làm cho chỉ mục bao phủ, do đó loại bỏ '...' trong danh sách chiếu yêu cầu tìm kiếm chỉ mục nhóm cần thiết, trong trường hợp chỉ mục không nhóm luôn luôn là chi phí phù hợp hơn so với quét nhóm.

Kimberley Tripp có bài viết trên blog bao gồm chủ đề này, cô gọi nó là 'index tipping point' giải thích cách một chỉ mục ứng cử viên hoàn toàn bị bỏ qua: chỉ mục không được phân nhóm không bao gồm danh sách dự chọn lọc sẽ được xem là tốn kém hơn so với quét theo nhóm.

+0

Cảm ơn thông tin và đề xuất. Nó cho tôi một số ý tưởng để xem xét. –

1

Thứ tự của các chỉ số quan trọng đối với truy vấn này:

CREATE INDEX MyIndex ON MyTable (col3 DESC, col2 ASC, col1 ASC) 

nó không quá nhiều ASC/DESC như rằng khi SQL Server đi để phù hợp với mệnh đề where, nó có thể phù hợp trên col3 đầu tiên và đi bộ chỉ số cùng giá trị đó.

+1

Đối với truy vấn này, thứ tự của chỉ mục phải giống như @Tom H. đã tạo nó. – Quassnoi

+0

Tôi đã thử một số lệnh có thể cho các cột. Tất cả đều cho kết quả tương tự. –

2

SQL Server ưu là không tốt trong o ptimizing truy vấn sử dụng các biến.

Nếu bạn chắc chắn rằng bạn sẽ luôn được hưởng lợi từ việc sử dụng chỉ mục, chỉ cần đặt gợi ý.

Nếu bạn sẽ đặt giá trị bằng chữ vào truy vấn thay vì biến, nó sẽ chọn số liệu thống kê chính xác và sẽ sử dụng chỉ mục.

Bạn cũng có thể cố gắng đưa một gợi ý nhiều ánh sáng:

OPTION (OPTIMIZE FOR (@col1 = 1, @col2 = 0, @col3 = '2009-07-09')) 

, mà sẽ tính toán kế hoạch thực hiện tốt nhất cho các giá trị của các biến, sử dụng thống kê, và sẽ không dính vào sử dụng chỉ số không có vấn đề gì.

+0

Nếu tôi chạy truy vấn bên ngoài SP, với các giá trị cột được mã hóa cứng, nó vẫn sử dụng quét chỉ mục nhóm: ( –

+0

@Tom: bạn có thể vui lòng đăng định nghĩa bảng chính xác không? – Quassnoi

1

Bạn đã cố gắng tung ra bit từ chỉ mục chưa?

create index ix1 on My_Table(Col3, Col1) INCLUDE(Col2) 
-- include other columns from the select list if needed 

Ngoài ra, bạn còn lại các cột còn lại trong danh sách chọn. Bạn có thể muốn xem xét bao gồm những thứ đó nếu không có nhiều trong chỉ mục hoặc như câu lệnh INCLUDE để tạo chỉ mục bao gồm cho truy vấn.

1

Hãy thử mặt nạ thông số của bạn để ngăn chặn paramter sniffing:

CREATE PROCEDURE MyProc AS 
    @Col1 INT 
    -- etc... 
AS 
    DECLARE @MaskedCol1 INT 
    SET @MaskedCol1 = @Col1 
    -- etc... 

    SELECT 
     col1, 
     col2… 
    FROM 
     dbo.My_Table 
    WHERE 
     col1 = @MaskecCol1 AND 
     -- etc... 

Âm thanh ngu ngốc nhưng tôi đã nhìn thấy SQL server làm một số điều kỳ lạ vì thông số đánh hơi.

+0

Cảm ơn bạn đã đề xuất.Tôi có thể chạy SELECT bên ngoài của SP mặc dù và tôi vẫn thấy tình hình tương tự. –

1

Tôi đặt cược SQL Server cho rằng giá của phần còn lại của các cột (được chỉ định bởi ... trong ví dụ của bạn) từ chỉ số nhóm lớn hơn lợi ích của chỉ mục để nó chỉ quét khóa được nhóm. Nếu có, hãy xem liệu bạn có thể biến nó thành chỉ mục bao trùm hay không.

Hay nó sử dụng một chỉ mục khác thay thế?

+0

Nó đang sử dụng khóa cụm nếu tôi không buộc sử dụng chỉ mục. Danh sách cột bao gồm tất cả các cột trong bảng. Trong khi tôi có thể đặt một chỉ số bao phủ lớn về điều đó, tôi sẽ có hiệu quả nhân đôi bảng. Tôi sẽ cần phải xem xét các tần số INSERT/UPDATE/DELETE để xem liệu chi phí có được đảm bảo hay không. –

0

Các cột có thể vô hiệu hóa không? Đôi khi Sql Server nghĩ rằng nó phải quét bảng để tìm giá trị NULL.

Thử thêm "và col1 không phải là null" vào truy vấn, nó sẽ làm cho sqlserver sử dụng gợi ý chỉ mục wtihout.

Ngoài ra, kiểm tra xem số liệu thống kê thực sự cập nhật:

SELECT 
    object_name = Object_Name(ind.object_id), 
    IndexName = ind.name, 
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id) 
FROM SYS.INDEXES ind 
order by STATS_DATE(ind.object_id, ind.index_id) desc 
+0

Không giống như Oracle, SQL Server lập chỉ mục NULL cũng như chỉ mục luôn bao gồm tất cả các hàng. – Quassnoi

+0

Lý thuyết của tôi là đôi khi thống kê cho thấy có nhiều hàng với NULL. Sau đó Sql Server quay trở lại một bảng quét để bao gồm các trường hợp "c1 là null". – Andomar

+0

@Andomar: truy vấn này không bao giờ có thể bao gồm các hàng mà 'COL1' là' NULL' – Quassnoi

0

Nếu SELECT của bạn trả về cột không có trong chỉ mục của bạn, SQL thấy rằng nó hiệu quả hơn để quét chỉ mục nhóm thay vì phải thực hiện tra cứu khóa để tìm các giá trị khác mà bạn đang yêu cầu.

Nếu bạn có cột TEXT, hãy thử chuyển kiểu dữ liệu thành VARCHAR (MAX), sau đó bao gồm các giá trị trong chỉ mục nonclustered.

Các vấn đề liên quan