2009-07-09 50 views
6

Tôi có câu hỏi về chỉ mục SQL Server. Tôi không phải là một DBA và giả sử câu trả lời là rõ ràng cho những người bạn của bạn. Tôi đang sử dụng SQL Server 2008.Thứ tự chỉ mục SQL Server (trường ngày giờ)

Tôi có một bảng mà là tương tự như sau (nhưng có nhiều cột):

CREATE TABLE [dbo].[Results](
    [ResultID] [int] IDENTITY(1,1) NOT NULL, 
    [TypeID] [int] NOT NULL, 
    [ItemID] [int] NOT NULL, 
    [QueryTime] [datetime] NOT NULL, 
    [ResultTypeID] [int] NOT NULL, 
    [QueryDay] AS (datepart(day,[querytime])) PERSISTED, 
    [QueryMonth] AS (datepart(month,[querytime])) PERSISTED, 
    [QueryYear] AS (datepart(year,[querytime])) PERSISTED, 
CONSTRAINT [PK_Results] PRIMARY KEY CLUSTERED 
(
    [ResultID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
) ON [PRIMARY] 

Các lĩnh vực cần lưu ý ở đây là ResultID, khóa chính, và QueryTime datetime mà tại đó kết quả được tạo ra.

Tôi cũng có chỉ số sau (giữa những người khác):

CREATE NONCLUSTERED INDEX [IDX_ResultDate] ON [dbo].[Results] 
(
    [QueryTime] ASC 
) 
INCLUDE ([ResultID], 
[ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 

Trong một cơ sở dữ liệu, nơi tôi có khoảng một triệu hàng trong bảng, chỉ số được sử dụng khi thực hiện một truy vấn như:

select top 1 * from results where querytime>'2009-05-01' order by ResultID asc 

Trong một trường hợp khác của cùng một cơ sở dữ liệu, với 50 triệu hàng, SQL Server quyết định không sử dụng chỉ mục vì nó thực hiện quét chỉ mục cụm mà kết thúc là chậm khủng khiếp. (và tốc độ phụ thuộc vào ngày). Ngay cả khi tôi sử dụng gợi ý truy vấn để làm cho nó sử dụng IDX_ResultDate, nó vẫn còn một chút chậm và nó dành 94% thời gian phân loại bởi ResultID. Tôi thấy rằng bằng cách tạo một chỉ mục với cả ResultID và QueryTime như các cột được sắp xếp trong chỉ mục, tôi có thể tăng tốc truy vấn của mình.

do đó tôi tạo ra như sau:

CREATE NONCLUSTERED INDEX [IDX_ResultDate2] ON [dbo].[Results] 
(
[QueryTime] ASC,  
[ResultID] ASC 
) 
INCLUDE ([ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
GO 

Tôi cho rằng nó đầu tiên sẽ sử dụng sắp xếp theo QueryTime để tìm ra kết quả phù hợp, mà đã có thể được sắp xếp theo ResultID. Tuy nhiên, đây không phải là trường hợp như chỉ số này thay đổi không có gì trong hiệu suất so với hiện tại.

sau đó tôi đã thử các chỉ số sau:

CREATE NONCLUSTERED INDEX [IDX_ResultDate3] ON [dbo].[Results] 
(
    [ResultID] ASC, 
    [QueryTime] ASC 
) 
INCLUDE ([ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
GO 

một này tạo ra kết quả dự kiến. Nó xuất hiện để trở lại trong thời gian không đổi (một phần nhỏ của một giây).

Tuy nhiên, tôi không hiểu tại sao IDX_ResultDate3 hoạt động tốt trong khi IDX_ResultDate2 thì không.

Tôi giả định rằng tìm kiếm nhị phân trong danh sách được sắp xếp của QueryTime theo sau bằng cách nhìn vào kết quả đầu tiên trong danh sách con của ResultID là cách nhanh nhất để nhận kết quả. (Do đó thứ tự sắp xếp ban đầu của tôi).

Câu hỏi phụ: Tôi có nên tạo cột được lưu giữ với phần ngày của QueryTime và chỉ mục trên đó thay thế (tôi đã có ba cột được duy trì như bạn có thể thấy ở trên) không?

Trả lời

12

Tôi giả định rằng tìm kiếm nhị phân trong dưới dạng danh sách được sắp xếp của QueryTime theo sau bằng cách nhìn vào kết quả đầu tiên trong số đó là danh sách con của ResultID là cách nhanh nhất để đạt được kết quả là . (Do đó, thứ tự sắp xếp ban đầu của tôi).

Điều đó sẽ được nhanh chóng thực sự, nhưng truy vấn của bạn thể hiện một yêu cầu khác nhau: Bạn đang yêu cầu các kết quả với ResultId tối thiểu từ tất cả các truy vấn mà xảy ra sau khi '2009/05/01'. Để đáp ứng yêu cầu nó phải tìm kiếm ở đầu phạm vi ('2009-05-01'), bắt đầu quét từ vị trí này để trích xuất tất cả ResultId, sắp xếp chúng sau đó trả về đầu trang 1 (ResultId tối thiểu). Chỉ mục thứ hai bạn đã thêm [idx_ResultDate2] cũng không giúp được gì nhiều. Truy vấn phải thực hiện khá nhiều chính xác tìm kiếm và quét tương tự: ResultIds được sắp xếp whithin ngày kết quả, do đó, để tìm ra ResultId hàng đầu từ tất cả kết quả sau '2009-05-01' truy vấn vẫn phải quét chỉ mục cho đến khi kết thúc.

Trên chỉ mục cuối cùng của bạn, [IDX_ResultDate3], truy vấn là gian lận. Những gì nó làm nó bắt đầu một quét trên inde và nó nhìn vào giá trị QueryTime, biết rằng trong chỉ số này quét đầu tiên Kết quả có QueryTime trong phạm vi mong muốn (> '2009-05-01') là một bạn muốn (vì ResultId được đảm bảo là Top 1). Bạn nhận được kết quả trong một 'phần nhỏ của một giây' từ may mắn tinh khiết: bạn có một Kết quả phù hợp ở đầu chỉ mục. Truy vấn cũng có thể quét toàn bộ chỉ mục và khớp với kết quả rất lat. Bạn có thể chèn một kết quả mới với QueryTime như '2010-01-01' và sau đó tìm kiếm nó, bạn sẽ thấy hiệu suất giảm xuống khi truy vấn phải quét toàn bộ chỉ mục cho đến cuối (vẫn nhanh hơn quét bảng vì kích thước chỉ số hẹp hơn).

Câu hỏi của tôi là: bạn có hoàn toàn tích cực rằng truy vấn của bạn phải trả về TOP 1 trong ORDER BY ResultID không? Hoặc bạn chỉ cần chọn theo thứ tự tùy ý? Nếu bạn có thể thay đổi yêu cầu ORDER BY thành QueryTime, thì bất kỳ chỉ mục nào (cập nhật: với QueryTime là cột ngoài cùng bên trái) sẽ trả về một Tìm kiếm và Tìm nạp đơn giản, không quét và không phân loại.

+0

Giải thích rất tốt. Giờ thì tôi đã hiểu. Tôi sẽ xem liệu tôi có thể tái thiết kế ứng dụng để sử dụng sắp xếp QueryTime hay không. –

2

Bạn có thể thay đổi chỉ số clustered đến ([QueryTime], [ResultID]), hoặc thay đổi truy vấn của bạn từ

select top 1 * from results where querytime>'2009-05-01' order by ResultID asc 

để

select top 1 <only the columns you actually need> from results where querytime>'2009-05-01' order by ResultID asc 

và bao gồm tất cả các cột trong [IDX_ResultDate2]

+1

+1 chính xác - chụp cho chỉ mục "bao phủ" bao gồm tất cả các trường cần thiết để đáp ứng truy vấn (nếu có thể) –

+0

Đúng, đã làm điều đó (không được đăng ở đây) nhưng cùng loại hiệu suất. –

4

Bạn có dao động điều kiện lọc trên một trường cùng với ORDER BY trường khác.

Không thể sử dụng chỉ mục, thậm chí chỉ mục tổng hợp để phân phát cả hai điều kiện trong trường hợp này.

Khi bạn tạo chỉ mục trên (queryTime, resultId), chỉ mục được sử dụng để lọc. Động cơ vẫn cần phải đặt hàng resultset.

Khi bạn tạo chỉ mục trên (resultId, queryTime), chỉ mục được sử dụng để đặt hàng.

Vì bạn cần kết quả TOP 1 và hàng satisifes kết quả này xảy ra ở đầu chỉ mục, cách tiếp cận thứ hai hóa ra hoạt động tốt hơn.

Nếu điều kiện lọc của bạn là chọn lọc (ví dụ: nó sẽ trả về một vài hàng) và kết quả đầu tiên bạn cần xảy ra ở cuối chỉ mục, lần đầu tiên được chấp thuận.

Xem bài viết này trong blog của tôi cho một số giải thích hơn và gợi ý về mà chỉ số để tạo ra trong những điều kiện nào:

+0

Bài đăng trên blog hay. –

0

Điều đầu tiên tôi xin đề nghị là để kiểm tra xem số liệu thống kê cho bảng này (tất cả các chỉ mục) được cập nhật.

Vì bạn nhận được hai kế hoạch thực hiện khác nhau với các tập dữ liệu khác nhau, có vẻ như SQL Server đang thực hiện một "cuộc gọi phán đoán" khét tiếng khi chọn một kế hoạch thực hiện khác.

Tôi đồng ý với lời giải thích của Remus về lý do tại sao bạn nhận được kết quả "huyền diệu" với chỉ mục cuối cùng của mình.

Đề xuất của anh ấy cũng tốt - bạn có thực sự muốn đặt hàng bởi resultID không? Hoặc nếu bạn có thể đặt hàng bởi queryTime, thì bạn sẽ có hiệu suất tốt hơn MUCH vì kế hoạch thực hiện sẽ có thể sử dụng thứ tự chỉ mục làm thứ tự của bộ kết quả (và nó sẽ tìm kiếm thông qua chỉ mục, so với quét).

+0

Có, số liệu thống kê được cập nhật. (và có, nó cần phải được sắp xếp ... thật không may!) –

0

Tôi không chắc tôi có thể trả lời câu hỏi nhưng sẽ chỉ ra rằng khóa chỉ mục được nhóm đã được bao gồm như một phần của bất kỳ chỉ mục nào khác, do đó dự phòng của nó bao gồm ResultID như một phần của bất kỳ chỉ mục nào khác mà bạn đề xuất.

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