SqlCommand.Prepare () kullanmanın anlamı ve faydası nedir?


14

SqlCommand.Prepare () (bkz. MSDN) yöntemi SQL sorgularının yürütülmesinden önce yaygın olarak kullanıldığı geliştirici kodu ile karşılaştım . Acaba bunun yararı nedir?

Örneklem:

command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();

Biraz oynadım ve izledim. Komutun Prepare()yöntemi çağırdıktan sonra yürütülmesi, Sql Server'ın aşağıdaki ifadeyi çalıştırmasını sağlar:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1

Bundan sonra Parametre değerini aldığında ve SqlCommand.ExecuteNonQuery()çağrıldığında, Sql-Server üzerinde aşağıdakiler yürütülür:

exec sp_execute 1,@id=20

Bana göre bu açıklama Prepare()yürütülür gibi bir tür derlenmiş alır gibi görünüyor . Acaba bunun yararı nedir? Bu, plan önbelleğine konulduğu ve son sorgu istenen parametre değerleriyle yürütüldüğünde tekrar kullanılabileceği anlamına mı geliyor?

SqlParameters ile yürütülen SqlCommands her zaman yordam çağrıları sarılmış olduğunu anladım (ve başka bir soru belgeledi ) sp_executesql. Bu, Sql Server'ın parametreleri parametre değerlerinden bağımsız olarak depolamasını ve yeniden kullanmasını sağlar.

Bununla ilgili olarak, prepare()yöntemin bir tür yararsız mı yoksa eski mi olduğunu veya burada bir şey eksik olup olmadığını merak ediyorum ?


Her zaman SQL Enjeksiyonu önlemek ile ilgili bir şey olduğunu düşündüm, bu yüzden belirli türlerde belirli değişkenleri kabul etmek için sorgu hazırlamak. Bu tür değişkenleri geçmezseniz, sorgu başarısız olur
Mark Sinkinson

Bununla birlikte, bu PHP açıklaması muhtemelen .NET php.net/manual/en/pdo.prepared-statements.php
Mark Sinkinson

"Evet, efendim. Sürücü, dışarı çıkmaya hazırlan." "Ne hazırlıyorsun ?! Her zaman hazırlıyorsun! Sadece git!"
Tüm

Yanıtlar:


19

Bir SQL grubunu hazırlanan SQL grubunu yürütmekten ayrı olarak hazırlamak etkili bir yapıdır **SQL Server için yürütme planlarının nasıl önbelleğe alındığı dikkate alınmaz. Hazırlık (ayrıştırma, herhangi bir parametreyi bağlama ve derleme) ve yürütme adımlarının ayrılması, yalnızca önbellek olmadığında mantıklıdır. Amaç, mevcut bir planı yeniden kullanarak ayrıştırma ve derleme için harcanan zamandan tasarruf etmektir ve dahili plan önbelleğinin yaptığı da budur. Ancak tüm RDBMS'ler bu önbellekleme düzeyini yapmaz (bu işlevlerin varlığından açıkça anlaşılır) ve bu nedenle bir planın "önbelleğe alınmasını" istemek ve bu önbellek kimliğini kaydetmek ve daha sonra yeniden kullanmak istemciye bırakılır. o. Bu, istemci kodu (ve bunu yapmayı ve doğru yapmayı hatırlamak için programcı) üzerinde ek bir yüktür. Aslında, IDbCommand.Prepare () yönteminin MSDN sayfasında şunlar belirtilir:

Sunucu yeniden kullanım planlarını otomatik olarak önbelleğe alır; bu nedenle, bu yöntemi doğrudan istemci uygulamanızda çağırmanıza gerek yoktur.

Kadarıyla benim test gösterdiği gibi çağırarak, (sen Soru gösterileceğini eşleşir), unutulmamalıdır SqlCommand.Prepare () yok, değil yapmak bir -sadece operasyonu "hazırlamak": Bu çağırır sp_prepexec hazırlar hangi ve SQL yürütür ; o aramazsa sp_prepare ayrıştırma ve sadece derlemek (ve herhangi bir parametre değerlerinin, sadece isimleri ve veri türleri içinde almaz). Bu nedenle, SqlCommand.Preparederhal yürütme yaptığı için (hedefin yürütmeyi ertelemek olduğunu varsayarak) çağrılmanın herhangi bir "derleme öncesi" yararı olamaz . ANCAK , bunun yerine SqlCommand.Prepare()arama sp_prepexecyapsa bile , arama yapmanın ilginç bir potansiyel küçük yararı vardır sp_prepare. Lütfen nota bakın (** ) ayrıntılar için altta.

Hatta benim test gösterileri SqlCommand.Prepare()denilen (ve bu çağrı lütfen unutmayın edilir değil , SQL Server üzerinde herhangi komutları), ExecuteNonQueryya da ExecuteReadero tamamen yürütülür izler ve ExecuteReader ise, bu satırları döndürür. Yine de, SQL Server Profiler, SqlCommand.Execute______()çağrıdan sonraki ilk çağrının SqlCommand.Prepare()"SQL Hazırla" olayı .Execute___()olarak kaydedildiğini ve sonraki çağrıların "Exec Prepared SQL" olayları olarak kaydedildiğini gösterir.


Test Kodu

PasteBin'e şu adresten yüklendi: http://pastebin.com/Yc2Tfvup . Kod, bir SQL Server Profiler izleme çalışırken (veya bir Genişletilmiş Olaylar oturumu) çalıştırılması amaçlanan bir .NET / C # Konsol Uygulaması oluşturur. Her adımdan sonra duraklar, böylece hangi ifadelerin belirli bir etkisi olduğu açıktır.


GÜNCELLEME

Daha fazla bilgi ve aramaktan kaçınmak için olası bir neden bulundu SqlCommand.Prepare(). Testlerimde fark ettiğim bir şey var ve bu test Konsolu Uygulamasını çalıştıran herkes için nelere dikkat edilmelidir: asla açık bir çağrı yapılmaz sp_unprepare. Biraz kazma yaptım ve MSDN forumlarında aşağıdaki yayını buldum:

SqlCommand - Hazırla ya da hazırlamıyor musun?

Kabul edilen cevap bir sürü bilgi içerir, ancak dikkat çekici noktalar şunlardır:

  • ** Hazırlanan şey sizi gerçekten kurtarır, öncelikle sorgu dizesini tel üzerinden iletmek için gereken süredir.
  • Hazırlanan bir sorgu, önbelleğe alınmış bir planın kaydedileceğini garanti etmez ve sunucuya ulaştığında geçici bir sorgudan daha hızlı değildir.
  • RPC'ler (CommandType.StoredProcedure) hazırlanırken hiçbir şey kazanmaz.
  • Sunucuda, hazırlanmış bir tanıtıcı temelde yürütülecek TSQL içeren bir bellek içi haritaya bir indekstir.
  • Harita her bağlantı için ayrı ayrı saklanır, çapraz bağlantı kullanımı mümkün değildir.
  • İstemci bağlantıyı havuzdan yeniden kullandığında gönderilen sıfırlama haritayı temizler.

SQLCAT ekibinden ilgili olduğu görülen ancak ODBC'ye özgü bir sorun olabileceği halde SqlClient, SqlConnection öğesinin atılmasıyla doğru şekilde temizlendiğinde de aşağıdaki gönderiyi buldum. SQLCAT direği, bunun bağlantı havuzunun temizlenmesi vb.

Hazırlanan SQL ifadelerine dikkat edin


SONUÇ

Verilen:

  • aramanın tek gerçek yararı SqlCommand.Prepare(), sorgu metnini tekrar ağ üzerinden göndermenize gerek olmadığıdır.
  • sorgu metnini çağırma sp_prepareve sp_prepexecbağlantının belleğinin bir parçası olarak kaydetme (örn. SQL Server belleği)

Tek potansiyel fayda olumsuz daha fazla sunucu belleği alıyor iken ağ paketleri kaydetme çünkü çağrı karşı tavsiye ederim SqlCommand.Prepare(). Çoğu durumda tüketilen bellek miktarı muhtemelen çok az olsa da, çoğu DB sunucusu en az 10 Megabit (bu günlerde 100 Megabit veya Gigabit) bağlantısı üzerinden doğrudan uygulama sunucularına bağlandığından ağ bant genişliği nadiren bir sorundur ( hatta bazıları aynı kutuda ;-). Bu ve bellek neredeyse her zaman ağ bant genişliğinden daha az kaynaktır.

Sanırım, birden çok veritabanına bağlanabilen yazılım yazan herkes için standart bir arayüzün rahatlığı bu maliyet / fayda analizini değiştirebilir. Ama bu durumda bile, bunu (örneğin değil dolayısıyla ve aralarındaki farklar, farklı sağlayıcılar sağlayan "standart" DB arabirimi soyut a kolay hala yeterince olduğuna inanmak zorunda ihtiyacı çağrısına Prepare()temel Nesne Bunu yaparken göz önüne alındığında) 'dir Uygulama kodunuzda muhtemelen yapmış olduğunuz yönlendirilmiş programlama ;-).


Bu kapsamlı cevap için teşekkür ederim. Adımlarınızı test ettim ve beklentilerinizden / sonuçlarınızdan iki sapma yaşadım: 4. adımda ek bir sonuç aldım. Adım 10'da adım 2'den daha başka bir plan_handle aldım
Magier

1
@Magier 4. adımdan emin değilsiniz ... belki farklı SET seçenekleriniz vardı veya ikisi arasındaki sorgu metni farklıydı? Tek bir karakter (görünür ya da değil) farkı bile farklı bir plan olacaktır. Bu web sayfasından kopyalayıp yapıştırmak yardımcı olmayabilir, bu nedenle sorguyu (tek tırnaklar arasındaki her şey) gelen sp_prepareçağrıya kopyalayın, sp_executesqlemin olmak için. Adım 10 için, evet, dün daha fazla test yaptım ve farklı tutamaçları olan bazı durumlar gördüm sp_prepare, ancak yine de asla aynı değil sp_executesql. Bir şeyi daha test edeceğim ve cevabımı güncelleyeceğim.
Solomon Rutzky

1
@Magier Aslında, daha önce yaptığım test kapsamındaydı ve planın gerçek bir yeniden kullanımı olduğunu düşünmüyorum, özellikle de MSDN forumunun ışığında sadece SQL'i önbelleğe aldığını söyleyerek. Hala plan_handle'ı nasıl kullanabileceğini merak ediyorum, oysa sp_executesqlyapamıyor ya da yapmıyor. Ama ne olursa olsun, sp_prepareplan önbellekten kaldırılmışsa ayrıştırma zamanı hala oradadır, bu yüzden cevabımın bu kısmını (ilginç ama alakasız) kaldıracağım. Bu bölüm, doğrudan sorunuza cevap vermediği için zaten ilginç bir yan nottu. Diğer her şey hala geçerlidir.
Solomon Rutzky

1
Bu aradığım açıklama türüydü.
CSharpie

5

Bu konuda merak ediyorum prepar () yöntemi bir tür yararsız mı yoksa eski mi yoksa burada bir şey eksik mi?

Prepare yöntem SqlCommand dünya sınırlı bir değere sahip olduğunu söyleyebilirim, tamamen işe yaramaz değil. Bir yararı Prepare, SqlCommand'ın uyguladığı IDbCommand arabiriminin bir parçasıdır. Bu, aynı kodun Prepare çağrılıp çağrılmayacağını belirlemek için koşullu mantık olmadan diğer DBMS sağlayıcılarına (Prepare gerektirebilir) çalışmasına izin verir.

Bu kullanım durumlarının yanı sıra AFAIK, SQL Server için .NET Sağlayıcısı ile Prepare'in değeri yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.