Tôi có một câu hỏi thú vị về SARGability. Trong trường hợp này, đó là về việc sử dụng một vị ngữ về sự khác biệt giữa hai cột ngày. Đây là thiết lập:
USE [tempdb]
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#sargme') IS NOT NULL
BEGIN
DROP TABLE #sargme
END
SELECT TOP 1000
IDENTITY (BIGINT, 1,1) AS ID,
CAST(DATEADD(DAY, [m].[severity] * -1, GETDATE()) AS DATE) AS [DateCol1],
CAST(DATEADD(DAY, [m].[severity], GETDATE()) AS DATE) AS [DateCol2]
INTO #sargme
FROM sys.[messages] AS [m]
ALTER TABLE [#sargme] ADD CONSTRAINT [pk_whatever] PRIMARY KEY CLUSTERED ([ID])
CREATE NONCLUSTERED INDEX [ix_dates] ON [#sargme] ([DateCol1], [DateCol2])
Những gì tôi sẽ thấy khá thường xuyên, là một cái gì đó như thế này:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... mà chắc chắn không phải là SARGable. Nó dẫn đến việc quét chỉ mục, đọc tất cả 1000 hàng, không tốt. Hàng ước tính bốc mùi. Bạn sẽ không bao giờ đưa điều này vào sản xuất.
Sẽ thật tuyệt nếu chúng ta có thể thực hiện các CTE, bởi vì điều đó sẽ giúp chúng ta thực hiện điều này, tốt hơn, SARGable-er, nói về mặt kỹ thuật. Nhưng không, chúng tôi có kế hoạch thực hiện tương tự như lên trên.
/*would be nice if it were sargable*/
WITH [x] AS ( SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) AS [ddif]
FROM
[#sargme] AS [s])
SELECT
*
FROM
[x]
WHERE
[x].[ddif] >= 48;
Và tất nhiên, vì chúng tôi không sử dụng hằng số, mã này không thay đổi gì và thậm chí không bằng một nửa SARGable. Không vui. Kế hoạch thực hiện tương tự.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Nếu bạn cảm thấy may mắn và bạn tuân theo tất cả các tùy chọn ANSI SET trong chuỗi kết nối của mình, bạn có thể thêm một cột được tính toán và tìm kiếm trên đó ...
ALTER TABLE [#sargme] ADD [ddiff] AS
DATEDIFF(DAY, DateCol1, DateCol2) PERSISTED
CREATE NONCLUSTERED INDEX [ix_dates2] ON [#sargme] ([ddiff], [DateCol1], [DateCol2])
SELECT [s].[ID] ,
[s].[DateCol1] ,
[s].[DateCol2]
FROM [#sargme] AS [s]
WHERE [ddiff] >= 48
Điều này sẽ giúp bạn tìm kiếm chỉ mục với ba truy vấn. Người đàn ông kỳ lạ là nơi chúng tôi thêm 48 ngày vào DateCol1. Truy vấn với DATEDIFF
trong WHERE
điều khoản, các CTE
, và truy vấn cuối cùng với một vị trên cột tính toán tất cả các cung cấp cho bạn một kế hoạch rất đẹp với ước tính đẹp hơn nhiều, và tất cả những gì.
Điều này đưa tôi đến câu hỏi: trong một truy vấn duy nhất, có cách nào SARGable để thực hiện tìm kiếm này không?
Không có bảng tạm thời, không có biến bảng, không thay đổi cấu trúc bảng và không có khung nhìn.
Tôi ổn với việc tự tham gia, CTE, truy vấn con hoặc nhiều lần truyền dữ liệu. Có thể hoạt động với mọi phiên bản SQL Server.
Tránh cột được tính toán là một giới hạn nhân tạo vì tôi quan tâm đến giải pháp truy vấn hơn bất kỳ điều gì khác.