Ước tính cardinality SARG, tại sao không quét toàn bộ?


11

Tại sao không có quét toàn bộ (Trên SQL 2008 R2 và 2012)?

Dữ liệu kiểm tra:

DROP TABLE dbo.TestTable
GO  
CREATE TABLE dbo.TestTable
(
   TestTableID INT IDENTITY PRIMARY KEY,
   VeryRandomText VarChar(50),
   VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
   Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
      Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
   Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
    VeryRandomText
)
Go

Khi thực hiện truy vấn:

Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad

Nhận cảnh báo (như mong đợi, vì so sánh dữ liệu nchar với cột varchar):

<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />

Nhưng sau đó tôi thấy kế hoạch thực hiện, và tôi có thể thấy, nó không sử dụng quét toàn bộ như tôi mong đợi, mà thay vào đó là tìm kiếm chỉ mục.

nhập mô tả hình ảnh ở đây

Tất nhiên, điều này là tốt, bởi vì trong trường hợp cụ thể này, việc thực thi nhanh hơn nhiều so với việc quét toàn bộ.

Nhưng tôi không thể hiểu làm thế nào máy chủ SQL đi đến quyết định thực hiện kế hoạch này.

Ngoài ra - nếu đối chiếu máy chủ sẽ là đối chiếu Windows ở cấp độ máy chủ và cấp độ cơ sở dữ liệu đối chiếu SQL Server, thì nó sẽ gây ra quét toàn bộ trên cùng một truy vấn.

Câu trả lời:


8

Khi so sánh các giá trị của các kiểu dữ liệu khác nhau, SQL Server tuân theo các quy tắc Ưu tiên Kiểu dữ liệu . Vì nvarchar có quyền ưu tiên cao hơn varchar, SQL Server phải chuyển đổi dữ liệu cột thành nvarchar trước khi so sánh các giá trị. Điều đó có nghĩa là áp dụng một hàm trên cột và điều đó sẽ làm cho truy vấn không thể thực hiện được.

Tuy nhiên, SQL Server làm điều tốt nhất để bảo vệ bạn khỏi những sai lầm của bạn, vì vậy, nó sử dụng một kỹ thuật được mô tả bởi Paul White trong bài đăng trên blog Tìm kiếm động và Chuyển đổi ẩn ẩn để tìm kiếm một loạt các giá trị và sau đó thực hiện so sánh cuối cùng, với chuyển đổi giá trị cột thành nvarchar, trong một vị từ dư để lọc ra bất kỳ giá trị dương nào.

Như bạn đã lưu ý, tuy nhiên điều này không hoạt động khi đối chiếu của cột là đối chiếu SQL. Lý do cho điều đó, tôi tin rằng, có thể được tìm thấy trong bài viết So sánh các đối chiếu SQL với các đối chiếu Windows

Về cơ bản, đối chiếu Windows sử dụng cùng một thuật toán cho varchar và nvarchar trong đó đối chiếu SQL sử dụng thuật toán khác nhau cho dữ liệu varchar và cùng thuật toán với đối chiếu Windows cho dữ liệu nvarchar.

Vì vậy, việc chuyển từ varchar sang nvarchar theo đối chiếu Windows sẽ sử dụng cùng một thuật toán và SQL Server có thể tạo ra một loạt các giá trị từ, trong trường hợp của bạn, một chữ nvarchar để lấy các hàng từ chỉ mục cột đối chiếu SQL varchar. Tuy nhiên, khi đối chiếu của cột varchar là đối chiếu SQL không thể thực hiện được do thuật toán khác nhau được sử dụng.


Cập nhật:

Một minh họa về các thứ tự sắp xếp khác nhau cho các cột varchar sử dụng đối chiếu windows và sql.

Câu đố SQL

Thiết lập lược đồ MS SQL Server 2014 :

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

Truy vấn 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Kết quả :

|   C |
|-----|
| a-b |
|  aa |
|  ac |

Truy vấn 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Kết quả :

|   C |
|-----|
|  aa |
| a-b |
|  ac |

0

Bạn phải nhớ rằng các nút lá của Chỉ mục không bao gồm các trang Chỉ mục chứa Khóa phân cụm hoặc RID để xác định vị trí hàng dữ liệu.

Trong mệnh đề where bạn nêu rõ VeryRandomText = N'111'Vì có một chỉ mục Không phân cụm trên VeryRandomText (tạo chỉ mục sẽ tạo chỉ mục không phân cụm trừ khi bạn nói rõ ràng để tạo một cụm), cách rẻ nhất để tìm dữ liệu là quét chỉ mục để tìm hàng và sau đó lấy dữ liệu cho hàng.

Nếu bạn sẽ tạo một chỉ mục cụm

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

hoặc khóa chính trên VeryRandomText, bạn sẽ có được bản quét chỉ mục đó.

Xem sách trực tuyến hoặc tại đây: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


Vâng, tôi biết những gì bạn viết. Như bạn có thể thấy, đã có chỉ mục được nhóm trên TestTableID. Nhưng vấn đề là - nếu máy chủ SQL không thể xem số liệu thống kê phân phối dữ liệu cột (như trong trường hợp này, do không khớp loại dữ liệu nên yêu cầu tất cả chuyển đổi loại dữ liệu giá trị hàng), thì nên chọn quét chỉ mục Clustered trong trường hợp này, không phải tìm kiếm chỉ mục .
Jāni

Và không phải lúc nào cũng rẻ nhất để tìm kiếm / quét chỉ mục không phân cụm - khi các giá trị không đủ khác biệt hoặc chỉ mục không bao phủ, thay vào đó có thể rẻ hơn để thực hiện quét chỉ mục theo cụm.
Jāni

@ Jāni không tham gia vào chỉ mục tạo tập lệnh của bạn sẽ không tạo ra một chỉ mục được nhóm mà bạn phải nói một cách rõ ràng - tương tự nếu bạn đọc kế hoạch truy vấn, tìm kiếm chỉ mục (không bao gồm)
Sporri

"Khi bạn tạo một ràng buộc PRIMARY KEY, một chỉ mục được nhóm duy nhất trên cột hoặc các cột sẽ tự động được tạo nếu chỉ mục cụm trên bảng chưa tồn tại và bạn không chỉ định một chỉ mục không bao gồm duy nhất." msdn.microsoft.com/en-us/l Library / ms186342.aspx
Jāni
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.