Ich bin auf Entwicklercode gestoßen, in dem die Methode SqlCommand.Prepare () (siehe MSDN) vor der Ausführung von SQL-Abfragen ausgiebig verwendet wird. Und ich frage mich, was der Nutzen davon ist?
Stichprobe:
command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();
Ich habe ein bisschen rumgespielt und nachgezeichnet. Die Ausführung des Befehls nach dem Aufruf der Prepare()
Methode bewirkt, dass Sql Server die folgende Anweisung ausführt:
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
Danach, wenn der Parameter seinen Wert erhält und SqlCommand.ExecuteNonQuery()
aufgerufen wird, wird Folgendes auf Sql-Server ausgeführt:
exec sp_execute 1,@id=20
Für mich sieht es so aus, als würde die Anweisung kompiliert, sobald sie Prepare()
ausgeführt wird. Ich frage mich, was ist der Vorteil davon? Bedeutet dies, dass es in den Plan-Cache gestellt wird und wieder verwendet werden kann, sobald die endgültige Abfrage mit den gewünschten Parameterwerten ausgeführt wird?
Ich habe herausgefunden (und in einer anderen Frage dokumentiert ), dass SqlCommands, die mit SqlParameters ausgeführt werden, immer in sp_executesql
Prozeduraufrufe eingeschlossen sind. Dadurch kann SQL Server die Pläne unabhängig von den Parameterwerten speichern und wiederverwenden.
In Bezug darauf frage ich mich, ob die prepare()
Methode nutzlos oder veraltet ist oder ob ich hier etwas vermisse?
Antworten:
Das separate Vorbereiten eines SQL-Stapels von der Ausführung des vorbereiteten SQL-Stapels ist ein Konstrukt, das effektiv ist **Für SQL Server nutzlos, da Ausführungspläne zwischengespeichert werden. Die Schritte der Vorbereitung (Parsen, Binden von Parametern und Kompilieren) und der Ausführung sind nur dann sinnvoll, wenn kein Caching stattfindet. Der Zweck besteht darin, die für das Parsen und Kompilieren aufgewendete Zeit zu sparen, indem ein vorhandener Plan wiederverwendet wird. Dies ist die Aufgabe des internen Plan-Cache. Da jedoch nicht alle RDBMS diese Zwischenspeicherebene (eindeutig aufgrund der Existenz dieser Funktionen) ausführen, muss der Clientcode anfordern, dass ein Plan "zwischengespeichert" wird, und diese Cache-ID speichern und dann erneut verwenden es. Dies ist eine zusätzliche Belastung für den Client-Code (und den Programmierer, sich daran zu erinnern, dies zu tun und es richtig zu machen), die nicht erforderlich ist. Die MSDN-Seite für die IDbCommand.Prepare () -Methode besagt tatsächlich :
Es sei darauf hingewiesen , dass, soweit meine Tests zeigt (was dem entspricht , was man in der Frage zeigen), Aufruf SqlCommand.Prepare () , wird nicht tun ein -nur Operation „vorbereiten“: es nennt sp_prepexec die vorbereitet und führt die SQL ; Es wird nicht sp_prepare aufgerufen, das nur analysiert und kompiliert wird (und keine Parameterwerte, nur deren Namen und Datentypen berücksichtigt). Daher kann es keinen Vorteil des Aufrufs vor dem Kompilieren geben,
SqlCommand.Prepare
da er eine sofortige Ausführung ausführt (vorausgesetzt, das Ziel besteht darin, die Ausführung aufzuschieben). JEDOCH gibt es ein interessantes Potential geringen Vorteil zu nennenSqlCommand.Prepare()
, auch wenn es nicht nennensp_prepexec
stattsp_prepare
. Bitte beachten Sie den Hinweis (** ) unten für Details.Meine Tests zeigen , dass selbst wenn
SqlCommand.Prepare()
genannt wird (bitte beachten Sie , dass dieser Aufruf nicht nicht erteilen alle Befehle auf SQL Server), dieExecuteNonQuery
oderExecuteReader
das folgt vollständig ausgeführt wird, und wenn es ExecuteReader ist, es gibt Reihen. SQL Server Profiler zeigt jedoch an, dass der ersteSqlCommand.Execute______()
Aufruf nach demSqlCommand.Prepare()
Aufruf als "Prepare SQL".Execute___()
-Ereignis registriert wird und nachfolgende Aufrufe als "Exec Prepared SQL" -Ereignisse registriert werden.Code testen
Es wurde auf PasteBin hochgeladen unter: http://pastebin.com/Yc2Tfvup . Der Code erstellt eine .NET / C # -Konsolenanwendung, die ausgeführt werden soll, während eine SQL Server Profiler-Ablaufverfolgung ausgeführt wird (oder eine Sitzung für erweiterte Ereignisse). Es wird nach jedem Schritt angehalten, damit klar wird, welche Anweisungen eine bestimmte Wirkung haben.
AKTUALISIEREN
Gefundene weitere Informationen und ein kleiner möglicher Grund, einen Anruf zu vermeiden
SqlCommand.Prepare()
. Eines ist mir bei meinen Tests aufgefallen, und was für jeden, der diese Testkonsolen-App ausführt, zu beachten ist: Es wird nie ein expliziter Anruf getätigtsp_unprepare
. Ich habe ein bisschen gegraben und den folgenden Beitrag in den MSDN-Foren gefunden:SqlCommand - Vorbereiten oder nicht vorbereiten?
Die akzeptierte Antwort enthält eine Reihe von Informationen, die wichtigsten Punkte sind jedoch:
Ich habe auch den folgenden Beitrag des SQLCAT-Teams gefunden, der verwandt zu sein scheint, aber einfach ein spezifisches Problem für ODBC sein könnte, während SqlClient beim Entsorgen von SqlConnection korrekt aufräumt. Es ist schwer zu sagen, da in der SQLCAT-Veröffentlichung keine zusätzlichen Tests erwähnt werden, die dies als Ursache belegen könnten, z. B. das Löschen des Verbindungspools usw.
Achten Sie auf die vorbereiteten SQL-Anweisungen
FAZIT
Vorausgesetzt, dass:
SqlCommand.Prepare()
darin, dass Sie den Abfragetext nicht erneut über das Netzwerk senden müssen.sp_prepare
undsp_prepexec
Speichern des Abfragetexts als Teil des Verbindungsspeichers (z. B. SQL Server-Speicher)Ich würde von Anrufen abraten,
SqlCommand.Prepare()
da der einzige potenzielle Vorteil darin besteht, Netzwerkpakete zu speichern, während der Nachteil mehr Serverspeicher beansprucht. Während der Speicherbedarf in den meisten Fällen wahrscheinlich sehr gering ist, ist die Netzwerkbandbreite selten ein Problem, da die meisten DB-Server über mindestens 10 Megabit (heutzutage eher 100 Megabit oder Gigabit) direkt mit den App-Servern verbunden sind. und manche sind sogar auf der gleichen Box ;-). Das und der Speicher sind fast immer eine knappere Ressource als die Netzwerkbandbreite.Ich nehme an, für alle, die Software schreiben, die eine Verbindung zu mehreren Datenbanken herstellen kann, könnte die Bequemlichkeit einer Standardschnittstelle diese Kosten-Nutzen-Analyse ändern. Aber auch in diesem Fall, ich habe zu glauben , dass es immer noch leicht genug , um zu abstrahieren „Standard“ DB - Schnittstelle ist , die für die verschiedenen Anbieter ermöglicht (und damit Unterschiede zwischen ihnen, wie nicht , um zu Anruf
Prepare()
) gegeben , dass dies ist ein grundlegende Object Orientierte Programmierung, die du wahrscheinlich schon in deinem App-Code machst ;-).quelle
sp_prepare
in den Aufruf ansp_executesql
, um sicherzugehen. Für Schritt 10, ja, ich habe gestern mehr Tests durchgeführt und habe einige Gelegenheiten mit unterschiedlichen Griffen für gesehensp_prepare
, aber immer noch nie die gleichen fürsp_executesql
. Ich werde noch eine Sache testen und meine Antwort aktualisieren.sp_executesql
kann oder nicht. Unabhängig davon ist die Analysezeit immer noch aktiv,sp_prepare
wenn der Plan aus dem Cache entfernt wurde, sodass ich diesen Teil meiner Antwort entfernen werde (interessant, aber irrelevant). Dieser Teil war sowieso eher eine interessante Randnotiz, da er Ihre direkte Frage nicht ansprach. Alles andere gilt noch.Ich würde sagen, dass die Prepare-Methode einen begrenzten Wert für SqlCommand-Welt hat, nicht, dass sie völlig nutzlos ist. Ein Vorteil ist, dass Prepare Teil der IDbCommand-Schnittstelle ist, die SqlCommand implementiert. Auf diese Weise kann derselbe Code mit anderen DBMS-Anbietern ausgeführt werden (für die möglicherweise Prepare erforderlich ist), ohne dass eine bedingte Logik erforderlich ist, um zu bestimmen, ob Prepare aufgerufen werden soll oder nicht.
Prepare hat beim .NET-Provider für SQL Server keinen Wert, abgesehen von diesen Anwendungsfällen, AFAIK.
quelle