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

Schnellste Methode für SQL Server-Einfügungen, Aktualisierungen und Auswahlen

Diese Antwort konzentriert sich hauptsächlich auf "Auswählen" im Vergleich zu Aktualisierungs-/Erstellungs-/Löschvorgängen. Ich denke, es ist seltener, mehr als einen oder ein paar Datensätze gleichzeitig zu aktualisieren, und daher denke ich auch, dass bei „select“ die Engpässe auftreten. Allerdings müssen Sie Ihre Anwendung (Profil) kennen. Der beste Ort, um Ihre Optimierungszeit zu konzentrieren, ist fast immer auf der Datenbankebene in den Abfragen selbst und nicht im Clientcode. Der Client-Code ist nur die Installation:Er ist nicht die Hauptkraft Ihrer App. Da Klempner jedoch dazu neigen, in vielen verschiedenen Apps wiederverwendet zu werden, sympathisiere ich mit dem Wunsch, es so nah wie möglich an das Optimum zu bringen, und daher habe ich viel zu sagen, wie man diesen Code erstellt.

Ich habe eine generische Methode für ausgewählte Abfragen/Prozeduren in meiner Datenschicht, die etwa so aussieht:

private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
    //ConnectionString is a private static property in the data layer
    // You can implement it to read from a config file or elsewhere
    using (var cn = new SqlConnection(ConnectionString))
    using (var cmd = new SqlCommand(sql, cn))
    {
        addParameters(cmd.Parameters);

        cn.Open();
        using (var rdr = cmd.ExecuteReader())
        {
            while (rdr.Read())
                yield return rdr;
            rdr.Close();
        }
    }
}

Und damit kann ich öffentliche Datenschichtmethoden schreiben, die anonyme Methoden verwenden, um die Parameter hinzuzufügen. Der gezeigte Code funktioniert mit .Net 2.0+, kann aber mit .Net 3.5 noch kürzer geschrieben werden:

public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
    //I could easily use a stored procedure name instead of a full sql query
    return Retrieve(
        @"SELECT c.* 
         FROM [ParentTable] p 
         INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
         WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
       {
          p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
       }
     );
}

Ich werde genau hier aufhören, damit ich Sie noch einmal auf den Code direkt oben verweisen kann, der die anonyme Methode zur Parametererstellung verwendet.

Dies ist ein sehr sauberer Code, da er die Abfragedefinition und die Parametererstellung an der gleichen Stelle platziert, während es Ihnen dennoch ermöglicht, die Boilerplate-Datenbankverbindung/den Aufrufcode an einen besser wiederverwendbaren Ort zu abstrahieren. Ich glaube nicht, dass diese Technik von einem der Aufzählungspunkte in Ihrer Frage abgedeckt wird, und sie ist auch verdammt schnell. Ich denke, das deckt ungefähr den Stoß Ihrer Frage ab.

Ich möchte jedoch weiter erklären, wie das alles zusammenpasst. Der Rest ist ziemlich einfach, aber es ist auch einfach, dies auf eine Liste oder ähnliches zu werfen und Dinge falsch zu machen, was letztendlich der Leistung schadet. Im weiteren Verlauf verwendet die Business-Schicht dann eine Factory, um Abfrageergebnisse in Objekte zu übersetzen (c# 3.0 oder höher):

public class Foo
{
    //various normal properties and methods go here

    public static Foo FooFactory(IDataRecord record)
    {
        return new Foo
        {
            Property1 = record[0],
            Property2 = record[1]
            //...
        };
    }
}

Anstatt diese in ihrer Klasse leben zu lassen, könnten Sie sie auch alle in einer statischen Klasse zusammenfassen, die speziell dafür vorgesehen ist, die Factory-Methoden zu enthalten.

Ich muss eine Änderung an der ursprünglichen Abrufmethode vornehmen. Diese Methode "ergibt" immer wieder das gleiche Objekt, und das funktioniert nicht immer so gut. Was wir anders machen wollen, damit es funktioniert, ist, eine Kopie des Objekts zu erzwingen, das durch den aktuellen Datensatz repräsentiert wird, sodass wir, wenn der Reader für den nächsten Datensatz mutiert, mit sauberen Daten arbeiten. Ich habe gewartet, bis ich die Fabrikmethode gezeigt hatte, damit wir sie im endgültigen Code verwenden können. Die neue Retrieve-Methode sieht folgendermaßen aus:

private static IEnumerable<T> Retrieve(Func<IDataRecord, T> factory,
                  string sql, Action<SqlParameterCollection> addParameters)
{
    //ConnectionString is a private static property in the data layer
    // You can implement it to read from a config file or elsewhere
    using (var cn = new SqlConnection(ConnectionString))
    using (var cmd = new SqlCommand(sql, cn))
    {
        addParameters(cmd.Parameters);

        cn.Open();
        using (var rdr = cmd.ExecuteReader())
        {
            while (rdr.Read())
                yield return factory(rdr);
            rdr.Close();
        }
    }
}

Und jetzt würden wir diese neue Retrieve()-Methode so aufrufen:

public IEnumerable<Foo> GetFooChildrenByParentID(int ParentID)
{
    //I could easily use a stored procedure name instead of a full sql query
    return Retrieve(Foo.FooFactory,
        @"SELECT c.* 
         FROM [ParentTable] p 
         INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
         WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
       {
          p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
       }
     );
}

Offensichtlich kann diese letzte Methode erweitert werden, um jede zusätzliche erforderliche Geschäftslogik einzubeziehen. Es stellt sich auch heraus, dass dieser Code außergewöhnlich schnell ist, da er die Lazy-Evaluation-Features von IEnumerable nutzt. Der Nachteil ist, dass es dazu neigt, viele kurzlebige Objekte zu erstellen, und das kann die Transaktionsleistung beeinträchtigen, nach der Sie gefragt haben. Um dies zu umgehen, unterbreche ich manchmal die gute n-Ebene und übergebe die IDataRecord-Objekte direkt an die Präsentationsebene und vermeide unnötige Objekterstellung für Datensätze, die einfach sofort an ein Grid-Steuerelement gebunden sind.

Code aktualisieren/erstellen ist ähnlich, mit dem Unterschied, dass Sie normalerweise nur einen Datensatz auf einmal ändern und nicht viele.

Oder ich könnte Ihnen das Lesen dieses langen Beitrags ersparen und Ihnen einfach sagen, dass Sie Entity Framework verwenden sollen;)