Diễn viên cho đến nay là sargable nhưng nó là một ý tưởng tốt?


47

Trong SQL Server 2008, kiểu dữ liệu ngày đã được thêm vào.

Đúc một datetimecột datesargable và có thể sử dụng một chỉ mục trên các datetimecột.

select *
from T
where cast(DateTimeCol as date) = '20130101';

Tùy chọn khác bạn có là sử dụng một phạm vi thay thế.

select *
from T
where DateTimeCol >= '20130101' and
      DateTimeCol < '20130102'

Những truy vấn này tốt như nhau hay nên được ưu tiên hơn các truy vấn khác?


4
Kế hoạch thực hiện nói gì?
a_horse_with_no_name

3
Tôi không thể nhận thấy rằng LINQ2Query tạo SQL where cast(date_column as date) = 'value'khi được trình bày với C # tương tự như where obj.date_column.Date == date_variable.
GSerg

6
Đó là một mục Kết nối tuyệt vời. :)
Rob Farley

1
Trang web Connect đã bị xóa cũng như Sargable trên Wikipedia
Ivanzinho

Câu trả lời:


59

Cơ chế đằng sau khả năng truyền tải của việc đúc cho đến nay được gọi là tìm kiếm động .

SQL Server gọi một hàm nội bộ GetRangeThroughConvertđể bắt đầu và kết thúc phạm vi.

Hơi ngạc nhiên khi đây không phải là phạm vi tương tự như các giá trị theo nghĩa đen của bạn.

Tạo một bảng với một hàng trên mỗi trang và 1440 hàng mỗi ngày

CREATE TABLE T
  (
     DateTimeCol DATETIME PRIMARY KEY,
     Filler      CHAR(8000) DEFAULT 'X'
  );

WITH Nums(Num)
     AS (SELECT number
         FROM   spt_values
         WHERE  type = 'P'
                AND number BETWEEN 1 AND 1440),
     Dates(Date)
     AS (SELECT {d '2012-12-30'} UNION ALL
         SELECT {d '2012-12-31'} UNION ALL
         SELECT {d '2013-01-01'} UNION ALL
         SELECT {d '2013-01-02'} UNION ALL
         SELECT {d '2013-01-03'})
INSERT INTO T
            (DateTimeCol)
SELECT DISTINCT DATEADD(MINUTE, Num, Date)
FROM   Nums,
       Dates 

Sau đó chạy

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT *
FROM   T
WHERE  DateTimeCol >= '20130101'
       AND DateTimeCol < '20130102'

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'; 

Truy vấn đầu tiên đã 1443đọc và truy vấn thứ hai 2883để nó đọc toàn bộ một ngày sau đó loại bỏ nó theo một vị từ còn lại.

Kế hoạch cho thấy vị ngữ tìm kiếm là

Seek Keys[1]: Start: DateTimeCol > Scalar Operator([Expr1006]), 
               End: DateTimeCol < Scalar Operator([Expr1007])

Vì vậy, thay vì >= '20130101' ... < '20130102'nó đọc > '20121231' ... < '20130102'sau đó loại bỏ tất cả các 2012-12-31hàng.

Một nhược điểm khác của việc dựa vào nó là các ước tính về số lượng thẻ có thể không chính xác như với truy vấn phạm vi truyền thống. Điều này có thể được nhìn thấy trong một phiên bản sửa đổi của SQL Fiddle của bạn .

Tất cả 100 hàng trong bảng hiện khớp với vị từ (với thời gian cách nhau 1 phút tất cả trong cùng một ngày).

Truy vấn thứ hai (phạm vi) ước tính chính xác rằng 100 sẽ khớp và sử dụng quét chỉ mục theo cụm. Các CAST( AS DATE)truy vấn sai ước tính rằng chỉ có một hàng sẽ phù hợp và tạo ra một kế hoạch với tra cứu chủ chốt.

Các số liệu thống kê không được bỏ qua hoàn toàn. Nếu tất cả các hàng trong bảng có cùng một vị trí datetimevà nó khớp với vị từ (ví dụ 20130101 00:00:00hoặc 20130101 01:00:00) thì kế hoạch hiển thị quét chỉ mục theo cụm với ước tính 31.6228 hàng.

100 ^ 0.75 = 31.6228

Vì vậy, trong trường hợp đó, nó xuất hiện ước tính được lấy từ công thức ở đây .

Nếu tất cả các hàng trong bảng đều giống nhau datetimevà nó không khớp với vị từ (ví dụ 20130102 01:00:00) thì nó sẽ rơi trở lại số hàng ước tính là 1 và kế hoạch có tra cứu.

Đối với các trường hợp trong đó bảng có nhiều hơn một DISTINCTgiá trị, các hàng ước tính dường như giống như khi truy vấn đang tìm kiếm chính xác 20130101 00:00:00.

Nếu biểu đồ thống kê xảy ra có một bước tại 2013-01-01 00:00:00.000đó thì ước tính sẽ được dựa trên EQ_ROWS(tức là không tính đến các thời điểm khác vào ngày đó). Mặt khác, nếu không có bước nào thì có vẻ như nó sử dụng AVG_RANGE_ROWStừ các bước xung quanh.

datetimecó độ chính xác xấp xỉ 3ms trong nhiều hệ thống, sẽ có rất ít giá trị trùng lặp thực tế và con số này sẽ là 1.


1
Xin chào Martin, bạn có thể thêm một TL;DRphần với một vài gạch đầu dòng với các trường hợp khác nhau để thêm vào trong trường hợp đó, dàn diễn viên cho đến nay có phải là một ý tưởng tốt hay không?
TT.

6
@TT. Tôi nghĩ vấn đề là nó không phải là một ý tưởng tốt. Tại sao bạn muốn sử dụng một phương pháp đòi hỏi một cheat sheet?
Aaron Bertrand

10

Tôi biết rằng điều này có một Great answer® lâu đời từ Martin, nhưng tôi muốn thêm vào một số thay đổi đối với hành vi ở đây trong các phiên bản SQL Server mới hơn. Điều này dường như chỉ được thử nghiệm cho đến 2008R2.

Với các GỢI Ý SỬ DỤNG mới giúp thực hiện một số chuyến du hành thời gian ước tính chính xác, chúng ta có thể thấy khi mọi thứ thay đổi.

Sử dụng cùng một thiết lập như trong SQL Fiddle.

CREATE TABLE T ( ID INT IDENTITY PRIMARY KEY, DateTimeCol DATETIME, Filler CHAR(8000) NULL );

CREATE INDEX IX_T_DateTimeCol ON T ( DateTimeCol );


WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
     E02(N) AS (SELECT 1 FROM E00 a, E00 b),
     E04(N) AS (SELECT 1 FROM E02 a, E02 b),
     E08(N) AS (SELECT 1 FROM E04 a, E04 b),
     Num(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY E08.N) FROM E08)
INSERT INTO T(DateTimeCol)
SELECT TOP 100 DATEADD(MINUTE, Num.N, '20130101')
FROM Num;

Chúng tôi có thể kiểm tra các cấp độ khác nhau như vậy:

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_100' ));
GO

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_120' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_140' ));
GO 

Các kế hoạch cho tất cả những thứ này có sẵn ở đây . Cả hai cấp độ compat 100 và 110 đều đưa ra kế hoạch tra cứu chính, nhưng bắt đầu với cấp độ compat 120, chúng tôi bắt đầu nhận cùng một kế hoạch quét với ước tính 100 hàng. Điều này đúng đến mức compat 140.

QUẢ HẠCH

QUẢ HẠCH

QUẢ HẠCH

Ước tính cardinality cho các >= '20130101', < '20130102'kế hoạch vẫn ở mức 100, dự kiến.

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.