SQL Server sorgu optimize edici, tekrarlanan hesaplanan değerleri tek bir Hesaplama Skaler işleci içinde birleştirebilir. Bunu yapıp yapmayacağı sorgu planı maliyetine ve hesaplanan değerin özelliklerine bağlıdır. Beklendiği gibi, belirsiz olmayan hesaplanmış değerler için bunu yapmayacaktır, örneğin birkaç istisna RAND(). Ayrıca kullanıcı tanımlı işlevler için bunu yapmaz.
Bir kullanıcı tanımlı fonksiyon örneği ile başlayacağım. İşte kullanıcı tanımlı fonksiyonun mükemmel bir örneği:
CREATE OR ALTER FUNCTION dbo.NULL_FUNCTION (@N BIGINT) RETURNS BIGINT
WITH SCHEMABINDING
AS
BEGIN
RETURN NULL;
END;
Ayrıca bir tablo oluşturmak ve içine 100 satır koymak istiyorum:
CREATE TABLE X_100 (N BIGINT NOT NULL);
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
INSERT INTO X_100 WITH (TABLOCK)
SELECT n
FROM Nums WHERE n <= 100;
dbo.NULL_FUNCTIONFonksiyon determistic olduğunu. Aşağıdaki sorgu için kaç kez yürütülecek?
SELECT n, dbo.NULL_FUNCTION(n)
FROM X_100;
Sorgu planına bağlı olarak, bu her satır için bir kez veya 100 kez yürütülür:

SQL Server 2016, sys.dm_exec_function_stats DMV'yi tanıttı . Bir sorgu tarafından bir UDF'nin kaç kez yürütüldüğünü görmek için bu DMV'nin anlık görüntülerini alabiliriz.
SELECT execution_count
FROM sys.dm_exec_function_stats
WHERE object_id = OBJECT_ID('NULL_FUNCTION');
Bunun sonucu 100'dür, bu nedenle işlev 100 kez yürütüldü.
Başka bir basit sorgu deneyelim:
SELECT n, dbo.NULL_FUNCTION(n), dbo.NULL_FUNCTION(n)
FROM X_100;
Sorgu planı, işlevin 200 kez yürütüleceğini önerir:

Sonuçlar sys.dm_exec_function_stats, işlevin 200 kez yürütüldüğünü göstermektedir.
Bir hesaplama skalerinin kaç kez yürütüldüğünü anlamak için her zaman sorgu planını kullanamayacağınızı unutmayın. Aşağıdaki alıntı " Hesaplamalı Skalerler, İfadeler ve Uygulama Planı Performansı " ndan alınmıştır:
Bu, Compute Skaler'ın diğer operatörlerin çoğunluğu gibi davrandığını düşünmeye yönlendirir: satırlar içinden aktıkça Compute Skaler'ın içerdiği hesaplamaların akışı akışa eklenir. Bu genellikle doğru değildir. İsme rağmen, Compute Scalar her zaman hiçbir şey hesaplamaz ve her zaman tek bir skaler değer içermez (örneğin bir vektör, bir takma ad veya hatta bir Boolean yüklemi olabilir). Daha sık olmamakla birlikte, bir Hesaplama Skaleri basitçe bir ifadeyi tanımlar; gerçek hesaplama, yürütme planında daha sonraki bir şeyin sonuca ihtiyacı olana kadar ertelenir.
Bir örnek daha deneyelim. Aşağıdaki sorgu için UDF bir kez hesaplanmasını umuyoruz:
WITH NULL_FUNCTION_CTE (NULL_VALUE) AS
(
SELECT DISTINCT dbo.NULL_FUNCTION(0)
)
SELECT n , cte.NULL_VALUE
FROM X_100
CROSS JOIN NULL_FUNCTION_CTE cte;
Sorgu planı, bir kez hesaplanacağını önerir:

Ancak, DMV gerçeği ortaya koymaktadır. Hesaplama skaleri, birleştirme işlecinde olana kadar ertelenir. 100 kez değerlendirilir.
Ayrıca, aynı ifadeyi birden çok kez yeniden hesaplamayı önlemek için optimize ediciyi teşvik etmek üzere neler yapabileceğinizi de sordunuz. Yapabileceğiniz en iyi şey, kodunuzda skaler UDF kullanmaktan kaçınmaktır. Bu soruların dışında, bellek hibelerini şişirmek, tüm sorguyu çalıştırmaya zorlamak MAXDOP 1, kötü kardinalite tahminleri ve ek CPU kullanımına yol açmak gibi bir dizi performans sorunu var . Bir UDF kullanmanız gerekiyorsa ve bu UDF'nin değeri bir sabitse, bunu sorgu dışında hesaplayabilir ve yerel bir değişkene koyabilirsiniz.
UDF içermeyen sorgular için, aynı sonucu döndüren ancak tam olarak aynı şekilde yazılmayan ifadeler yazmaktan kaçınmayı deneyebilirsiniz. Bu sonraki örnek için halka açık AdventureworksDW2016CTP3 veritabanını kullanıyorum, ancak gerçekten herhangi bir veritabanı yapacak. COUNT(*)Bu sorgu için kaç kez hesaplanacak?
SELECT OrderDateKey, COUNT(*)
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;
Bu sorgu için Hash Match (toplu) operatörüne bakarak bunu çözebiliriz.

COUNT(*)Benzersiz her değer için bir kez hesaplanır OrderDateKey. ORDER BYMaddenin dahil edilmesi , iki kez hesaplanmasına neden olmaz. İcra planını burada görebilirsiniz .
Şimdi, aynı sonuçları döndürecek ancak farklı bir şekilde yazılmış bir sorgu düşünün:
SELECT OrderDateKey, SUM(1)
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;
Sorgu iyileştirici bunları birleştirecek kadar akıllı değildir, bu nedenle ek işler yapılacaktır:
