Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wann ist es besser, Ad-hoc-SQL als gespeicherte Prozeduren zu schreiben?

SQL Server speichert die Ausführungspläne für Ad-hoc-Abfragen, sodass (abzüglich der Zeit, die der erste Aufruf benötigt) die beiden Ansätze hinsichtlich der Geschwindigkeit identisch sind.

Im Allgemeinen bedeutet die Verwendung gespeicherter Prozeduren, dass Sie einen Teil des von Ihrer Anwendung benötigten Codes (die T-SQL-Abfragen) an einen Ort legen, der nicht der Quellcodeverwaltung unterliegt (es kann sein, aber normalerweise nicht ) und wo es von anderen ohne Ihr Wissen geändert werden kann.

Die Abfragen an einem zentralen Ort wie diesem zu haben, kann eine gute Sache sein, je nachdem, wie viele verschiedene Anwendungen Zugriff auf die Daten benötigen, die sie repräsentieren. Ich finde es im Allgemeinen viel einfacher, die von einer Anwendung verwendeten Abfragen im Anwendungscode selbst zu belassen.

Mitte der 1990er-Jahre hieß es allgemein, dass gespeicherte Prozeduren in SQL Server der richtige Weg in leistungskritischen Situationen seien, und damals war das definitiv der Fall. Die Gründe hinter diesem CW sind jedoch schon lange nicht mehr gültig.

Aktualisierung: Außerdem wird in Debatten über die Lebensfähigkeit gespeicherter Prozeduren häufig die Notwendigkeit angeführt, SQL-Injection zu verhindern, um Procs zu verteidigen. Sicherlich denkt niemand, der bei klarem Verstand ist, dass das Zusammenstellen von Ad-hoc-Abfragen durch Zeichenfolgenverkettung das Richtige ist (obwohl Sie dadurch nur dann einem SQL-Injection-Angriff ausgesetzt sind, wenn Sie Benutzereingaben verketten ). Offensichtlich sollten Ad-hoc-Abfragen parametrisiert werden, nicht nur um das Monster unter dem Bett eines SQL-Injection-Angriffs zu verhindern, sondern auch, um Ihr Leben als Programmierer allgemein einfacher zu machen (es sei denn, Sie müssen gerne herausfinden, wann Sie single Anführungszeichen um Ihre Werte).

Aktualisierung 2: Ich habe mehr recherchiert. Basierend auf diesem MSDN-Whitepaper , es scheint, dass die Antwort davon abhängt, was Sie mit Ihren Abfragen genau unter "ad-hoc" verstehen. Zum Beispiel eine einfache Abfrage wie diese:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... wird seinen Ausführungsplan zwischenspeichern lassen. Da die Abfrage keine bestimmten disqualifizierenden Elemente enthält (wie fast alles andere als ein einfaches SELECT aus einer Tabelle), wird SQL Server die Abfrage tatsächlich "automatisch parametrisieren" und die Literalkonstante "5" durch einen Parameter und Cache ersetzen den Ausführungsplan für die parametrisierte Version. Das heißt, wenn Sie dann this ausführen Ad-hoc-Abfrage:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

... es wird in der Lage sein, den zwischengespeicherten Ausführungsplan zu verwenden.

Leider ist die Liste der disqualifizierenden Abfrageelemente für die automatische Parametrisierung lang (vergessen Sie beispielsweise die Verwendung von DISTINCT , TOP , UNION , GROUP BY , OR usw.), also können Sie sich wirklich nicht auf die Leistung verlassen.

Wenn Sie eine "superkomplexe" Abfrage haben, die nicht automatisch parametrisiert wird, wie:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

... es wird immer noch durch den genauen Text der Abfrage zwischengespeichert. Wenn Ihre Anwendung diese Abfrage also wiederholt mit denselben wörtlichen "hartcodierten" Werten aufruft, verwendet jede Abfrage nach der ersten den zwischengespeicherten Ausführungsplan erneut (und also so schnell wie eine gespeicherte Prozedur).

Wenn sich die Literalwerte ändern (z. B. basierend auf Benutzeraktionen wie Filtern oder Sortieren angezeigter Daten), profitieren die Abfragen nicht vom Caching (außer gelegentlich, wenn sie versehentlich genau mit einer kürzlich durchgeführten Abfrage übereinstimmen).

Um vom Caching mit "Ad-hoc"-Abfragen zu profitieren, besteht die Möglichkeit, sie zu parametrisieren. Erstellen einer Abfrage in C# im Handumdrehen wie folgt:

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

ist falsch. Der richtige Weg (mit ADO.Net) wäre etwa so:

using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}

Die Abfrage enthält keine Literale und ist bereits vollständig parametrisiert, sodass nachfolgende Abfragen mit der identischen parametrisierten Anweisung den zwischengespeicherten Plan verwenden würden (selbst wenn sie mit anderen Parameterwerten aufgerufen würden). Beachten Sie, dass der Code hier praktisch derselbe ist wie der Code, den Sie ohnehin zum Aufrufen einer gespeicherten Prozedur verwenden würden (der einzige Unterschied besteht in CommandType und CommandText), also kommt es etwas darauf an, wo der Text dieser Abfrage "leben" soll " (in Ihrem Anwendungscode oder in einer gespeicherten Prozedur).

Wenn Sie schließlich mit "Ad-hoc"-Abfragen meinen, dass Sie dynamisch Abfragen mit verschiedenen Spalten, Tabellen, Filterparametern und so weiter erstellen, wie vielleicht diese:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC

... dann können Sie es so gut wie nicht tun Sie dies mit gespeicherten Prozeduren (ohne die EXEC Hack, von dem in der feinen Gesellschaft nicht die Rede sein darf), also ist der Punkt strittig.

Aktualisierung 3: Hier ist die einzige wirklich gute leistungsbezogene Grund (den ich mir sowieso vorstellen kann) für die Verwendung einer gespeicherten Prozedur. Wenn es sich bei Ihrer Abfrage um eine lang andauernde Abfrage handelt, bei der das Kompilieren des Ausführungsplans erheblich länger dauert als die eigentliche Ausführung, und die Abfrage nur selten aufgerufen wird (z. B. ein monatlicher Bericht), kann es sein, dass Sie sie in eine gespeicherte Prozedur einfügen Sorgen Sie dafür, dass SQL Server den kompilierten Plan so lange im Cache belässt, dass er noch im nächsten Monat verfügbar ist. Ob das stimmt oder nicht, ist mir allerdings schleierhaft.