Saya memiliki pertanyaan menarik tentang SARGability. Dalam hal ini, ini tentang menggunakan predikat pada perbedaan antara dua kolom tanggal. Ini pengaturannya:
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])
Apa yang akan sering saya lihat, adalah sesuatu seperti ini:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... yang pastinya bukan SARGable. Ini menghasilkan pemindaian indeks, membaca semua 1000 baris, tidak baik. Baris yang diperkirakan bau. Anda tidak akan pernah memproduksinya.
Alangkah baiknya jika kita bisa mewujudkan CTE, karena itu akan membantu kita membuat ini, yah, lebih SARGable-er, secara teknis. Tapi tidak, kami mendapatkan rencana eksekusi yang sama seperti di bagian atas.
/*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;
Dan tentu saja, karena kita tidak menggunakan konstanta, kode ini tidak mengubah apa pun, dan bahkan tidak separuh SARGable. Tidak menyenangkan. Paket eksekusi yang sama.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Jika Anda merasa beruntung, dan mematuhi semua opsi SET ANSI di string koneksi Anda, Anda dapat menambahkan kolom yang dihitung, dan mencarinya ...
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
Ini akan membuat Anda mencari indeks dengan tiga pertanyaan. Orang aneh keluar adalah tempat kami menambahkan 48 hari ke DateCol1. Kueri dengan DATEDIFF
dalam WHERE
klausa, CTE
dan, kueri akhir dengan predikat pada kolom yang dikomputasi semua memberi Anda rencana yang jauh lebih baik dengan perkiraan yang jauh lebih bagus, dan semua itu.
Yang membawa saya ke pertanyaan: dalam satu permintaan, apakah ada cara SARGable untuk melakukan pencarian ini?
Tidak ada tabel temp, tidak ada variabel tabel, tidak mengubah struktur tabel, dan tidak ada tampilan.
Saya baik-baik saja dengan self-joins, CTE, subqueries, atau multiple pass atas data. Dapat bekerja dengan versi SQL Server apa pun.
Menghindari kolom yang dikomputasi adalah batasan buatan karena saya lebih tertarik pada solusi kueri daripada yang lainnya.