SARGability hakkında ilginç bir soru olan ben var. Bu durumda, iki tarih sütunu arasındaki farkın bir tahminini kullanmakla ilgilidir. İşte kurulum:
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])
Çok sık göreceğim şey şunun gibi:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... kesinlikle SARGable değil. İndeks taramasıyla sonuçlanır, tüm 1000 satırları okur, hiç iyi değil. Tahmini satırlar kokuyor. Bunu asla üretime sokmazdın.
CTE'leri gerçekleştirebilmemiz iyi olurdu, çünkü bu teknik olarak daha iyi, daha SARGable yapmamıza yardımcı olacak. Fakat hayır, aynı uygulama planını üstümüze alıyoruz.
/*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;
Ve elbette, sabitleri kullanmadığımız için, bu kod hiçbir şeyi değiştirmez ve yarı SARGable bile değildir. Eğlenceli değil. Aynı uygulama planı.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Kendinizi şanslı hissediyorsanız ve bağlantı dizelerinizdeki tüm ANSI SET seçeneklerine uyuyorsanız, bir hesaplanmış sütun ekleyebilir ve üzerinde arama yapabilirsiniz ...
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
Bu size üç sorgudan oluşan bir endeks araması yapacaktır. Tuhaf adam, DateCol1'e 48 gün eklediğimiz yer. İle sorgu DATEDIFF
içinde WHERE
fıkra, CTE
hesaplanmış bir sütun üzerinde bir yüklemi ve son sorgu tüm size çok daha güzel tahminleri ile çok daha hoş bir plan vermek, vesaire.
Hangisi soruyu gündeme getiriyor: tek bir sorguda, bu aramayı gerçekleştirmek için SARGable bir yol var mı?
Temp tabloları yok, tablo değişkenleri yok, tablo yapısını değiştirmek yok ve görünüm yok.
Ben kendiliğinden bir araya gelme, CTE'ler, alt sorgular veya veri üzerinden çoklu geçişler konusunda iyiyim. SQL Server'ın herhangi bir sürümü ile çalışabilir.
Hesaplanan sütundan kaçınmak yapay bir sınırlamadır, çünkü bir sorgulama çözümüyle her şeyden daha çok ilgileniyorum.