Cải thiện hiệu năng của truy vấn bằng IN ()


14

Tôi có truy vấn SQL sau:

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

Tôi cũng có một chỉ mục trên Eventbảng cho cột TimeStamp. Hiểu biết của tôi là chỉ số này không được sử dụng vì IN()tuyên bố. Vì vậy, câu hỏi của tôi là có cách nào để tạo một chỉ mục cho IN()tuyên bố cụ thể này để tăng tốc truy vấn này không?

Tôi cũng đã thử thêm Event.EventTypeID IN (2, 5, 7, 8, 9, 14)dưới dạng bộ lọc cho chỉ mục trên TimeStamp, nhưng khi nhìn vào kế hoạch thực hiện, nó dường như không sử dụng chỉ mục này. Bất kỳ đề xuất hoặc cái nhìn sâu sắc về điều này sẽ được đánh giá rất cao.

Dưới đây là kế hoạch đồ họa:

Kế hoạch thực hiện

Và đây là một liên kết đến tập tin .sqlplan .


Chúng ta cũng có thể nhìn vào kế hoạch thực hiện chứ? :)
dezso

1
Và xin vui lòng gửi kế hoạch thực hiện thực tế (không ước tính) với phần mở rộng .sqlplan. Hầu hết mọi người chỉ muốn đăng một ảnh chụp màn hình của kế hoạch đồ họa, và điều đó ít hữu ích hơn nhiều.
Aaron Bertrand

OK Tôi đã thêm một kế hoạch thực hiện cũng như cập nhật truy vấn SQL.
SandersKY

@SandersKY Tốt nhất nên đặt nội tuyến tệp .sqlplan để giữ mọi thứ liên quan đến câu hỏi trên cùng một trang.
Trygve Laugstøl

1
@trygvis - Điều đó thường không thể thực hiện được do giới hạn độ dài trên các bài đăng. Trao đổi ngăn xếp xấu hổ không hỗ trợ lưu trữ bài viết đính kèm trong nội bộ.
Martin Smith

Câu trả lời:


18

Cho các bảng có dạng tổng quát sau:

CREATE TABLE Device 
(
    ID integer PRIMARY KEY
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(50) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    [TimeStamp] datetime NOT NULL, 
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device
);

Các chỉ số sau đây là hữu ích:

CREATE INDEX f1 
ON [Event] ([TimeStamp], EventTypeID) 
INCLUDE (DeviceID)
WHERE EventTypeID IN (2, 5, 7, 8, 9, 14);

Đối với truy vấn:

SELECT
  [Event].ID,
  [Event].[TimeStamp],
  EventType.Name,
  Device.ID
FROM
  [Event]
INNER JOIN EventType ON EventType.ID = [Event].EventTypeID
INNER JOIN Device ON Device.ID = [Event].DeviceID
WHERE
  [Event].[TimeStamp] BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.EventTypeID IN (2, 5, 7, 8, 9, 14);

Bộ lọc đáp ứng ANDyêu cầu mệnh đề, khóa đầu tiên của chỉ mục cho phép tìm kiếm đối [TimeStamp]với bộ lọc EventTypeIDsvà bao gồm DeviceIDcột làm cho chỉ số bao phủ (vì DeviceIDđược yêu cầu cho phép nối với Devicebảng).

Hoàn thành kế hoạch

Khóa thứ hai của chỉ mục - EventTypeIDkhông bắt buộc (nó cũng có thể là một INCLUDEdcột); Tôi đã bao gồm nó trong khóanhững lý do được nêu ở đây . Nói chung, tôi khuyên mọi người ít nhất là INCLUDEcác cột từ một WHEREmệnh đề chỉ mục được lọc .


Dựa trên kế hoạch thực hiện và truy vấn được cập nhật trong câu hỏi, tôi đồng ý rằng chỉ số chung hơn được đề xuất bởi SSMS có thể là lựa chọn tốt hơn ở đây, trừ khi danh sách được lọc EventTypeIDslà tĩnh vì Aaron cũng đề cập đến câu trả lời của mình:

CREATE TABLE Device 
(
    ID integer PRIMARY KEY,
    Name nvarchar(50) NOT NULL UNIQUE
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(20) NOT NULL UNIQUE,
    [Description] nvarchar(100) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    PLCTimeStamp datetime NOT NULL,
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device,
    IATA varchar(50) NOT NULL,
    Data1 integer NULL,
    Data2 integer NULL,
);

Chỉ mục được đề xuất (khai báo là duy nhất nếu phù hợp):

CREATE UNIQUE INDEX uq1
ON [Event]
    (EventTypeID, PLCTimeStamp)
INCLUDE 
    (DeviceID, IATA, Data1, Data2, ID);

Thông tin về thẻ từ kế hoạch thực hiện (cú pháp không có giấy tờ, không sử dụng trong các hệ thống sản xuất):

UPDATE STATISTICS dbo.Event WITH ROWCOUNT = 4042700, PAGECOUNT = 400000;
UPDATE STATISTICS dbo.EventType WITH ROWCOUNT = 22, PAGECOUNT = 1;
UPDATE STATISTICS dbo.Device WITH ROWCOUNT = 2806, PAGECOUNT = 28;

Truy vấn được cập nhật (lặp lại INdanh sách cho EventTypebảng giúp trình tối ưu hóa trong trường hợp cụ thể này):

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2,
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND EventType.ID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

Kế hoạch thực hiện dự kiến:

Kế hoạch thứ hai

Kế hoạch bạn nhận được có thể sẽ khác bởi vì tôi đang sử dụng số liệu thống kê đoán. Điểm chung là cung cấp cho trình tối ưu hóa càng nhiều thông tin càng tốt và cung cấp phương thức truy cập (chỉ mục) hiệu quả trên [Event]bảng hàng 4 triệu .


8

Phần lớn chi phí là quét chỉ mục theo cụm và trừ khi bảng này thực sự rộng hoặc bạn không thực sự cần tất cả các cột trong đầu ra, tôi tin rằng SQL Server là đường dẫn tối ưu trong kịch bản hiện tại không có gì khác thay đổi . Nó sử dụng quét phạm vi (được gắn nhãn là tìm kiếm CI) để thu hẹp phạm vi các hàng mà nó quan tâm, nhưng vì đầu ra, nó vẫn sẽ yêu cầu tra cứu hoặc quét CI ngay cả với chỉ mục được lọc mà bạn đã tạo được nhắm mục tiêu trong phạm vi này và ngay cả trong trường hợp đó, quét CI có thể vẫn rẻ nhất (hoặc ít nhất là SQL Server ước tính như vậy).

Kế hoạch thực hiện cho bạn biết rằng chỉ số này sẽ hữu ích:

CREATE NONCLUSTERED INDEX ix_EventTypeID_PLCTimeStamp_WithIncludes
  ON [dbo].[Event] ([EventTypeID],[PLCTimeStamp])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

Mặc dù tùy thuộc vào dữ liệu của bạn, nó có thể tốt hơn theo cách khác, ví dụ:

CREATE NONCLUSTERED INDEX ix_PLCTimeStamp_EventTypeID_WithIncludes
  ON [dbo].[Event] ([PLCTimeStamp],[EventTypeID])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

Nhưng tôi sẽ kiểm tra cả hai để chắc chắn cái nào tốt hơn, nếu một trong hai - sự khác biệt giữa một trong hai chỉ số đó và những gì bạn có bây giờ chỉ có thể là cận biên (quá nhiều biến số để chúng tôi biết) và bạn phải tính đến việc bổ sung chỉ mục yêu cầu bảo trì thêm và điều này có thể ảnh hưởng đáng kể đến các hoạt động DML của bạn (chèn / cập nhật / xóa). Bạn cũng có thể xem xét bao gồm các tiêu chí bộ lọc trong chỉ mục này theo đề xuất của @QueryKiwi , nhưng chỉ khi đó là tập hợp các giá trị EventTypeID mà bạn tìm kiếm thường xuyên. Nếu bộ đó thay đổi theo thời gian, thì chỉ mục được lọc sẽ chỉ hữu ích cho truy vấn cụ thể này.

Với số lượng hàng thấp như vậy, tôi phải tự hỏi làm thế nào hiệu suất có thể hiện tại có thể xấu? Truy vấn này trả về 3 hàng (nhưng không có bất kỳ dấu hiệu nào cho thấy có bao nhiêu hàng bị từ chối). Có bao nhiêu hàng trong bảng?


4

Tôi chỉ phát hiện ra rằng SQL Server 2008 R2 thực sự đã đưa ra một đề xuất chỉ mục khi tôi chạy kế hoạch thực hiện. Chỉ mục được đề xuất này làm cho truy vấn chạy nhanh hơn khoảng 90%.

Chỉ số mà nó gợi ý là như sau:

CREATE NONCLUSTERED INDEX [INDEX_spBagSearch] ON [dbo].[Event] 
(
    [EventTypeID] ASC,
    [PLCTimeStamp] ASC
)
INCLUDE ( [ID],
[DeviceID],
[Data1],
[Data2],
[IATA]) 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) ON [PRIMARY]
GO
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.