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 :
Der Server speichert die Pläne automatisch zwischen, damit sie bei Bedarf wiederverwendet werden können. Daher ist es nicht erforderlich, diese Methode direkt in Ihrer Clientanwendung aufzurufen.
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 nennen SqlCommand.Prepare()
, auch wenn es nicht nennen sp_prepexec
statt sp_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), die ExecuteNonQuery
oder ExecuteReader
das folgt vollständig ausgeführt wird, und wenn es ExecuteReader ist, es gibt Reihen. SQL Server Profiler zeigt jedoch an, dass der erste SqlCommand.Execute______()
Aufruf nach dem SqlCommand.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ätigt sp_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:
- ** Die Vorbereitung spart Ihnen in erster Linie die Zeit, die Sie für die drahtlose Übertragung der Abfragezeichenfolge benötigen.
- Eine vorbereitete Abfrage garantiert nicht, dass ein zwischengespeicherter Plan gespeichert wird, und ist nicht schneller als eine Ad-hoc-Abfrage, sobald sie den Server erreicht.
- RPCs (CommandType.StoredProcedure) profitieren nicht von der Vorbereitung.
- Auf dem Server ist ein vorbereitetes Handle im Grunde ein Index für eine speicherinterne Zuordnung, die TSQL enthält, das ausgeführt werden soll.
- Die Karte wird pro Verbindung gespeichert, eine Querverbindung ist nicht möglich.
- Durch das Zurücksetzen, das gesendet wird, wenn der Client die Verbindung aus dem Pool erneut verwendet, wird die Zuordnung gelöscht.
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:
- Der einzige wirkliche Vorteil eines Anrufs besteht
SqlCommand.Prepare()
darin, dass Sie den Abfragetext nicht erneut über das Netzwerk senden müssen.
- Aufrufen
sp_prepare
und sp_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 ;-).