2013-08-05 18 views
8

Tôi hy vọng hai số SELECT sẽ có cùng một kế hoạch thực hiện và hiệu suất. Vì có một ký tự đại diện hàng đầu trên LIKE, tôi mong đợi quét chỉ mục. Khi tôi chạy điều này và xem xét các kế hoạch, SELECT hoạt động như mong đợi (với một lần quét). Nhưng kế hoạch SELECT thứ hai cho thấy chỉ mục tìm kiếm và chạy nhanh hơn gấp 20 lần.Cách LIKE '% ...' có thể tìm kiếm chỉ mục như thế nào?

Code:

-- Uses index scan, as expected: 
SELECT 1 
    FROM AccountAction 
    WHERE AccountNumber LIKE '%441025586401' 

-- Uses index seek somehow, and runs much faster: 
declare @empty VARCHAR(30) = '' 
SELECT 1 
    FROM AccountAction 
    WHERE AccountNumber LIKE '%441025586401' + @empty 

Câu hỏi:

Làm thế nào để SQL Server sử dụng một chỉ mục tìm kiếm khi mô hình bắt đầu với một ký tự đại diện?

Bonus câu hỏi:

Tại sao concatenating một sự thay đổi chuỗi rỗng/cải thiện kế hoạch thực hiện?

chi tiết:

  • Có một chỉ số không clustered trên Accounts.AccountNumber
  • Có các chỉ số khác, nhưng cả hai tìm kiếm và quét là trên chỉ số này.
  • Cột Accounts.AccountNumber là một nullable varchar(30)
  • Máy chủ là SQL Server 2012

Bảng và chỉ số định nghĩa:

CREATE TABLE [updatable].[AccountAction](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [AccountNumber] [varchar](30) NULL, 
    [Utility] [varchar](9) NOT NULL, 
    [SomeData1] [varchar](10) NOT NULL, 
    [SomeData2] [varchar](200) NULL, 
    [SomeData3] [money] NULL, 
    --... 
    [Created] [datetime] NULL, 
CONSTRAINT [PK_Account] PRIMARY KEY NONCLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 


CREATE NONCLUSTERED INDEX [IX_updatable_AccountAction_AccountNumber_UtilityCode_ActionTypeCd] ON [updatable].[AccountAction] 
(
    [AccountNumber] ASC, 
    [Utility] ASC 
) 
INCLUDE ([SomeData1], [SomeData2], [SomeData3]) 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] 


CREATE CLUSTERED INDEX [CIX_Account] ON [updatable].[AccountAction] 
(
    [Created] 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] 

LƯU Ý: Dưới đây là kế hoạch thực hiện thực tế cho hai truy vấn. Tên của các đối tượng hơi khác so với mã ở trên vì tôi đang cố giữ cho câu hỏi đơn giản.

The two execution plans.

+1

là có sự khác biệt trong thực hiện thực tế hoặc chỉ trong kế hoạch dự toán? –

+3

@GordonLinoff Số phiên bản SQL Server 2012 là 11, 2008 R2: 10.5, 2008: 10, v.v. – swasheck

+0

Tôi không biết tầm quan trọng của nó, nhưng các truy vấn mà bạn thực sự chạy là 'LIKE '% 441025586401%'' , với ký tự đại diện ở phần bắt đầu và kết thúc – Lamak

Trả lời

7

Các xét nghiệm (cơ sở dữ liệu AdventureWorks2008R2) cho thấy những gì xảy ra:

SET NOCOUNT ON; 
SET STATISTICS IO ON; 

PRINT 'Test #1'; 
SELECT p.BusinessEntityID, p.LastName 
FROM Person.Person p 
WHERE p.LastName LIKE '%be%'; 

PRINT 'Test #2'; 
DECLARE @Pattern NVARCHAR(50); 
SET @Pattern=N'%be%'; 
SELECT p.BusinessEntityID, p.LastName 
FROM Person.Person p 
WHERE p.LastName LIKE @Pattern; 

SET STATISTICS IO OFF; 
SET NOCOUNT OFF; 

Kết quả:

Test #1 
Table 'Person'. Scan count 1, logical reads 106 
Test #2 
Table 'Person'. Scan count 1, logical reads 106 

Kết quả từ SET STATISTICS IO cho thấy LIO là cùng. Nhưng kế hoạch thực hiện khá khác nhau: enter image description here

Trong thử nghiệm đầu tiên, SQL Server sử dụng một Index Scan rõ ràng nhưng trong lần thứ hai thử nghiệm SQL Server sử dụng một Index Seek mà là một Index Seek - range scan. Trong trường hợp cuối cùng SQL Server sử dụng một nhà điều hành Compute Scalar để tạo ra những giá trị

[Expr1005] = Scalar Operator(LikeRangeStart([@Pattern])), 
[Expr1006] = Scalar Operator(LikeRangeEnd([@Pattern])), 
[Expr1007] = Scalar Operator(LikeRangeInfo([@Pattern])) 

và, việc sử dụng Index Seek điều hành một Seek Predicate (tối ưu hóa) cho một range scan (LastName > LikeRangeStart AND LastName < LikeRangeEnd) cộng với một unoptimized Predicate (LastName LIKE @pattern).

Làm cách nào để LIKE '% ...' tìm kiếm chỉ mục?

Câu trả lời của tôi: nó không phải là "thực" Index Seek. Đó là Index Seek - range scan, trong trường hợp này, có cùng hiệu suất như Index Scan.

Vui lòng xem, sự khác biệt giữa Index SeekIndex Scan (tranh luận tương tự): So…is it a Seek or a Scan?.

Sửa 1: Các kế hoạch thực hiện cho OPTION(RECOMPILE) (xem khuyến nghị của Aaron xin) cho thấy, cũng có thể, một Index Scan (thay vì Index Seek): enter image description here

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