Tại sao SQL Server bỏ qua một chỉ mục?


16

Tôi có một bảng, CustPassMastervới 16 cột trong đó, một trong số đó CustNum varchar(8)và tôi đã tạo một chỉ mục IX_dbo_CustPassMaster_CustNum. Khi tôi chạy SELECTtuyên bố của mình :

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

Nó bỏ qua chỉ số hoàn toàn. Điều này làm tôi bối rối khi tôi có một bảng khác CustDataMastervới nhiều cột hơn (55), một trong số đó là CustNum varchar(8). Tôi đã tạo một chỉ mục trên cột này ( IX_dbo_CustDataMaster_CustNum) trong bảng này và thực tế sử dụng cùng một truy vấn:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

Và nó sử dụng chỉ mục tôi tạo ra.

Có bất kỳ lý do cụ thể đằng sau này? Tại sao nó sẽ sử dụng chỉ mục từ CustDataMaster, nhưng không phải là từ CustPassMaster? Có phải do số lượng cột thấp?

Truy vấn đầu tiên trả về 66 hàng. Đối với hàng thứ hai, 1 hàng được trả lại.

Ngoài ra, lưu ý bổ sung: CustPassMastercó 4991 hồ sơ và CustDataMastercó 5376 hồ sơ. Đây có thể là lý do đằng sau bỏ qua các chỉ số? CustPassMastercũng có bản ghi trùng lặp có cùng CustNumgiá trị. Đây có phải là một yếu tố khác?

Tôi đang dựa trên yêu cầu này về kết quả kế hoạch thực hiện thực tế của cả hai truy vấn.

Đây là DDL cho CustPassMaster(cái có chỉ số không sử dụng):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Và DDL cho CustDataMaster(Tôi đã bỏ qua rất nhiều lĩnh vực không liên quan):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Tôi không có một chỉ mục được nhóm trên một trong các bảng đó, chỉ có một chỉ mục không bao gồm.

Bỏ qua thực tế là các kiểu dữ liệu không hoàn toàn khớp với loại dữ liệu được lưu trữ. Các trường này là một bản sao lưu từ cơ sở dữ liệu IBM AS / 400 và đây là các kiểu dữ liệu tương thích cho nó. (Tôi phải có thể truy vấn cơ sở dữ liệu sao lưu này với cùng một truy vấn và nhận được kết quả chính xác như nhau .)

Dữ liệu này chỉ được sử dụng cho các SELECTbáo cáo. Tôi không thực hiện bất kỳ INSERT/ UPDATE/ DELETEtuyên bố nào về nó, ngoại trừ khi ứng dụng sao lưu đang sao chép dữ liệu từ AS / 400.


Có thể đáng để đọc bài viết này về điểm tới hạn từ NonClustered đến Clustered. sqlskills.com/bloss/kimberly/the-tipping-point-query-answers
Mark Sinkinson

3
Vì vậy, đó là sự khác biệt. Nếu truy vấn đầu tiên sử dụng chỉ mục của bạn, nó sẽ phải thực hiện 65 lần tra cứu. Cái này đắt quá. Truy vấn thứ hai chỉ phải thực hiện một.
Aaron Bertrand

Câu trả lời:


18

Thông thường các chỉ mục sẽ được SQL Server sử dụng nếu sử dụng chỉ mục này hợp lý hơn là sử dụng trực tiếp bảng bên dưới.

Có vẻ như trình tối ưu hóa dựa trên chi phí nghĩ rằng sẽ thực sự tốn kém hơn khi sử dụng chỉ số được đề cập. Bạn có thể thấy nó sử dụng chỉ mục nếu thay vì làm SELECT *, bạn chỉ đơn giản SELECT T1Col1.

Khi bạn SELECT *đang yêu cầu SQL Server trả về tất cả các cột trong bảng. Để trả về các cột đó, SQL Server phải đọc các trang cho các hàng khớp với WHEREtiêu chí câu lệnh từ chính bảng đó (chỉ mục cụm hoặc heap). SQL Server có lẽ đang nghĩ số lượng đọc cần thiết để lấy phần còn lại của các cột từ bảng có nghĩa là nó cũng có thể quét bảng trực tiếp. Sẽ rất hữu ích khi xem truy vấn thực tế và kế hoạch thực hiện thực tế được sử dụng bởi truy vấn.


3
Vì vậy, một giải pháp rõ ràng và tối ưu hơn sẽ là cho tôi để giới hạn các cột tôi chọn và đưa chúng vào INCLUDEmệnh đề của chỉ mục?
Der Kommissar

1
Điều đó rất có thể tạo ra một sự khác biệt lớn. Việc thêm tất cả các cột được truy vấn trả về vào INCLUDEmệnh đề có thể sẽ khiến SQL Server sử dụng chỉ mục. Đã nói rằng, những gì bạn đang cố gắng để tối ưu hóa? Đối với tôi, dường như bảng của bạn có kích thước hàng trung bình là 100 byte, thì 5000 hàng chỉ có khoảng 500kb dữ liệu và có thể không đáng để dành thời gian cho nó.
Max Vernon

1
Kích thước hàng trung bình là 0,30KB đối với Table1và 0,53KB đối với Table2. Tất cả dữ liệu này được nhập từ AS / 400 (Hệ thống IBM i) và KHÔNG có PK trên bất cứ thứ gì. Tôi đã tự tạo tất cả các chỉ mục ngày hôm nay sau khi mọi người đề cập rằng ứng dụng này khá chậm.
Der Kommissar

10

Để sử dụng chỉ mục, vì bạn đang thực hiện select *, trước tiên SQL Server phải đọc từng hàng từ chỉ mục khớp với giá trị bạn có trong mệnh đề where. Dựa trên điều này, nó sẽ nhận được các giá trị chỉ mục được phân cụm cho từng hàng và sau đó nó phải tìm kiếm từng giá trị riêng biệt với chỉ mục được phân cụm (= tra cứu khóa). Vì bạn nói rằng các giá trị không phải là duy nhất, SQL Server sử dụng số liệu thống kê để ước tính số lần phải thực hiện tra cứu khóa này.

Rất có thể là ước tính chi phí để quét chỉ mục không được phân cụm + tra cứu khóa vượt quá ước tính chi phí cho quét chỉ mục được phân cụm và đó là lý do tại sao chỉ mục bị bỏ qua.

Bạn có thể thử sử dụng set statistics io onvà sau đó sử dụng một gợi ý chỉ mục để xem liệu chi phí I / O có thực sự nhỏ hơn khi sử dụng chỉ mục hay không. Nếu sự khác biệt là lớn, bạn có thể xem xét số liệu thống kê, nếu những điều đó đã lỗi thời.

Ngoài ra, nếu SQL của bạn thực sự sử dụng các biến và không phải là các giá trị chính xác, điều này cũng có thể được gây ra bởi việc đánh hơi tham số (= giá trị trước đó được sử dụng để tạo kế hoạch có rất nhiều hàng trong bảng).


1

Đó có thể là lý do. Các trình tối ưu hóa dựa trên chi phí và quyết định chọn đường dẫn nào dựa trên 'chi phí' mà mỗi đường dẫn thực hiện có. Chi phí 'lớn nhất' là đưa dữ liệu từ đĩa vào bộ nhớ. Nếu trình tối ưu hóa tính toán rằng cần nhiều thời gian hơn để đọc cả chỉ mục và dữ liệu thì nó có thể quyết định bỏ qua chỉ mục. Các hàng càng lớn thì càng có nhiều khối đĩa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.