"Maliyet" varsayımı zaman açısındandır (ancak ;-) açısından başka ne olabileceğinden emin olmasa da, en azından aşağıdaki gibi bir şey yaparak bunu anlayabilmelisiniz:
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
SET STATISTICS TIME ON;
EXEC sp_help 'sys.databases'; -- replace with your proc
SET STATISTICS TIME OFF;
"Mesajlar" sekmesinde bildirilen ilk öğe şu şekilde olmalıdır:
SQL Server ayrıştırma ve derleme zamanı:
Ben bunu en az 10 kez çalıştırmak ve hem "CPU" hem de "Geçen" milisaniye ortalama.
İdeal olarak bunu Üretim'de çalıştırırsınız, böylece gerçek zamanlı bir tahmin elde edebilirsiniz, ancak nadiren insanların Üretim'deki plan önbelleğini temizlemelerine izin verilir. Neyse ki, SQL Server 2008'den başlayarak, belirli bir planı önbellekten temizlemek mümkün oldu. Bu durumda aşağıdakileri yapabilirsiniz:
DECLARE @SQL NVARCHAR(MAX) = '';
;WITH cte AS
(
SELECT DISTINCT stat.plan_handle
FROM sys.dm_exec_query_stats stat
CROSS APPLY sys.dm_exec_text_query_plan(stat.plan_handle, 0, -1) qplan
WHERE qplan.query_plan LIKE N'%sp[_]help%' -- replace "sp[_]help" with proc name
)
SELECT @SQL += N'DBCC FREEPROCCACHE ('
+ CONVERT(NVARCHAR(130), cte.plan_handle, 1)
+ N');'
+ NCHAR(13) + NCHAR(10)
FROM cte;
PRINT @SQL;
EXEC (@SQL);
SET STATISTICS TIME ON;
EXEC sp_help 'sys.databases' -- replace with your proc
SET STATISTICS TIME OFF;
Bununla birlikte, "kötü" önbelleğe alınmış plana neden olan parametreler için geçirilen değerlerin değişkenliğine bağlı olarak, aralarında bir orta nokta olduğunu düşünmek için başka bir yöntem vardır. OPTION(RECOMPILE)
ve OPTION(OPTIMIZE FOR UNKNOWN)
Dinamik SQL. Evet, söyledim. Ve hatta parametreleştirilmemiş Dinamik SQL demek. İşte nedeni.
En azından bir veya daha fazla giriş parametresi değeri açısından eşit olmayan bir dağılımı olan verileriniz açıkça var. Bahsedilen seçeneklerin dezavantajları şunlardır:
OPTION(RECOMPILE)
her yürütme için bir plan oluşturur ve tekrar iletilen parametre değerleri önceki çalıştırma (lar) ile aynı olsa bile hiçbir zaman planın yeniden kullanımından asla yararlanamazsınız . Sıklıkla çağrılan proc'lar için - birkaç saniyede bir veya daha sık - bu sizi ara sıra korkunç durumlardan koruyacaktır, ancak yine de sizi her zaman o kadar da harika olmayan bir durumda bırakacaktır.
OPTION(OPTIMIZE FOR (@Param = value))
bu özel değere dayalı bir plan oluşturacak ve bu da birkaç duruma yardımcı olabilir, ancak sizi şu anki konuya açık bırakacaktır.
OPTION(OPTIMIZE FOR UNKNOWN)
ortalama bir dağılımın ne kadar olduğuna bağlı olarak bazı sorgulara yardımcı olacak, ancak diğerlerine zarar verecek bir plan oluşturacaktır. Bu, yerel değişkenleri kullanma seçeneğiyle aynı olmalıdır.
Bununla birlikte, dinamik SQL, doğru bir şekilde yapıldığında , geçirilen çeşitli değerlerin ideal olan kendi ayrı sorgu planlarına sahip olmalarını sağlayacaktır (olacakları kadar). Buradaki ana maliyet, geçirilen değerlerin çeşitliliği arttıkça, önbellekteki yürütme planlarının sayısının artması ve belleğin yer almasıdır. Düşük maliyetler:
SQL Enjeksiyonlarını önlemek için dize parametrelerini doğrulama ihtiyacı
Dinamik SQL doğrudan tablo izinleri gerektirdiğinden ideal güvenlik soyutlamasını korumak için bir Sertifika ve Sertifika Tabanlı Kullanıcı kurması gerekebilir.
Yani, saniyede birden fazla çağrılan ve her biri milyonlarca satır içeren birden çok tabloya çarptığım proclarım olduğunda bu durumu nasıl yönettim. Denedim OPTION(RECOMPILE)
ama parametre koklama / kötü önbelleğe alınmış plan sorunu olmayan olguların% 99'unda bu sürece çok zarar verdi. Ve lütfen bu süreçlerden birinin içinde yaklaşık 15 sorgu bulunduğunu ve yalnızca 3 - 5'inin burada açıklandığı gibi Dinamik SQL'e dönüştürüldüğünü unutmayın; Belirli bir sorgu için gerekli olmadıkça dinamik SQL kullanılmadı.
Saklı yordam için birden çok giriş parametresi varsa, hangilerinin çok farklı veri dağılımlarına sahip (ve dolayısıyla bu soruna neden olan) sütunlarla kullanıldığını ve hangilerinin daha eşit dağılımlı (ve olmamalıdır) sütunlarla kullanıldığını öğrenin bu soruna neden olan).
Eşit olarak dağıtılmış sütunlarla ilişkilendirilmiş proc giriş parametreleri için parametreleri kullanarak Dinamik SQL dizesini oluşturun. Bu parametrelendirme, bu sorgu ile ilgili önbellekteki yürütme planlarında ortaya çıkan artışın azaltılmasına yardımcı olur.
Çok çeşitli dağılımlarla ilişkili kalan parametreler için, bunlar dinamik SQL olarak değişmez değerler olarak birleştirilmelidir. Benzersiz bir sorgu, sorgu metninde yapılan herhangi bir değişiklikle belirlendiğinden, sahip WHERE StatusID = 1
olmak , sahip olmaktan farklı bir sorgu ve dolayısıyla farklı sorgu planıdır WHERE StatusID = 2
.
Sorgunun metnine birleştirilecek proc giriş parametrelerinden herhangi biri dizeler ise, SQL Enjeksiyonuna karşı koruma sağlamak için doğrulanmaları gerekir (ancak, geçirilen dizeler, bir kullanıcı değil, yine de). En azından REPLACE(@Param, '''', '''''')
tek tırnakların tek tırnaklardan kaçmasını sağlamak için yapın.
Gerekirse, bir Kullanıcı oluşturmak için kullanılacak bir Sertifika oluşturun ve saklı yordamı, [public]
aksi takdirde bu tür izinlere sahip olmaması gereken Kullanıcılara değil, yalnızca yeni Sertifika Tabanlı Kullanıcıya verilecek şekilde saklayın. .
Örnek proc:
CREATE PROCEDURE MySchema.MyProc
(
@Param1 INT,
@Param2 DATETIME,
@Param3 NVARCHAR(50)
)
AS
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'
SELECT tab.Field1, tab.Field2, ...
FROM MySchema.SomeTable tab
WHERE tab.Field3 = @P1
AND tab.Field8 >= CONVERT(DATETIME, ''' +
CONVERT(NVARCHAR(50), @Param2, 121) +
N''')
AND tab.Field2 LIKE N''' +
REPLACE(@Param3, N'''', N'''''') +
N'%'';';
EXEC sp_executesql
@SQL,
N'@P1 INT',
@P1 = @Param1;