Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Najszybsza metoda wstawiania, aktualizowania i wybierania programu SQL Server

Ta odpowiedź skupia się głównie na operacjach „wybierz” w porównaniu z aktualizacją/utworzeniem/usunięciem. Myślę, że rzadsze jest aktualizowanie więcej niż jednego lub kilku rekordów naraz, dlatego uważam, że „wybierz” to miejsce, w którym pojawiają się wąskie gardła. To powiedziawszy, musisz znać swoją aplikację (profil). Najlepszym miejscem do skupienia się na optymalizacji jest prawie zawsze poziom bazy danych w samych zapytaniach, a nie kod klienta. Kod klienta to tylko hydraulika:to nie jest główna siła Twojej aplikacji. Ponieważ jednak kanalizacja ma tendencję do ponownego wykorzystywania w wielu różnych aplikacjach, sympatyzuję z pragnieniem, aby była jak najbardziej zbliżona do optymalnej, dlatego mam wiele do powiedzenia na temat tego, jak zbudować ten kod.

Mam ogólną metodę zapytań/procedur wybierających w mojej warstwie danych, która wygląda mniej więcej tak:

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();
        }
    }
}

A to pozwala mi pisać publiczne metody warstwy danych, które używają anonimowych metod do dodawania parametrów. Pokazany kod działa z .Net 2.0+, ale można go napisać jeszcze krócej przy użyciu .Net 3.5:

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;
       }
     );
}

Zatrzymam się w tym miejscu, aby ponownie wskazać powyższy kod, który wykorzystuje anonimową metodę tworzenia parametrów.

Jest to bardzo czysty kod, ponieważ umieszcza definicję zapytania i tworzenie parametrów w tym samym miejscu, jednocześnie umożliwiając oddzielenie kodu połączenia/wywołania bazy danych w miejsce, które można ponownie wykorzystać. Nie sądzę, że ta technika jest objęta żadnym z punktów w twoim pytaniu, i okazuje się, że jest również cholernie szybka. Myślę, że to dotyczy istoty twojego pytania.

Chcę jednak kontynuować, aby wyjaśnić, jak to wszystko do siebie pasuje. Reszta jest dość prosta, ale łatwo jest też wrzucić to na listę lub coś podobnego i popełnić błąd, ostatecznie obniżając wydajność. Idąc dalej, warstwa biznesowa używa następnie fabryki do tłumaczenia wyników zapytań na obiekty (c# 3.0 lub nowszy):

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]
            //...
        };
    }
}

Zamiast mieć je w swojej klasie, możesz również zgrupować je wszystkie razem w klasę statyczną przeznaczoną specjalnie do przechowywania metod fabrycznych.

Muszę dokonać jednej zmiany w pierwotnej metodzie pobierania. Ta metoda „wydaje” ten sam obiekt w kółko, a to nie zawsze działa tak dobrze. To, co chcemy zrobić inaczej, aby to zadziałało, to wymuszenie kopii obiektu reprezentowanego przez bieżący rekord, tak aby po zmutowaniu czytnika dla następnego rekordu pracowaliśmy z czystymi danymi. Poczekałem, aż pokażę metodę fabryczną, żebyśmy mogli użyć jej w ostatecznym kodzie. Nowa metoda pobierania wygląda tak:

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();
        }
    }
}

A teraz moglibyśmy wywołać tę nową metodę Retrieve() w następujący sposób:

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;
       }
     );
}

Oczywiście ta ostatnia metoda może zostać rozszerzona o dodatkową potrzebną logikę biznesową. Okazuje się również, że ten kod jest wyjątkowo szybki, ponieważ wykorzystuje leniwe funkcje oceny IEnumerable. Minusem jest to, że ma tendencję do tworzenia wielu krótkotrwałych obiektów, a to może zaszkodzić wydajności transakcyjnej, o którą pytałeś. Aby obejść ten problem, czasami łamię dobrą n-warstwę i przekazuję obiekty IDataRecord bezpośrednio do warstwy prezentacji i unikam niepotrzebnego tworzenia obiektów dla rekordów, które są po prostu od razu powiązane z kontrolką siatki.

Aktualizuj/utwórz kod jest podobny, z tą różnicą, że zwykle zmieniasz tylko jeden rekord naraz, a nie wiele.

Albo mógłbym zaoszczędzić Ci czytania tego długiego posta i po prostu powiedzieć Ci, abyś używał Entity Framework;)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyjaśnienie ANSI_NULLS programu SQL Server

  2. Generuj diagram relacji tabeli z istniejącego schematu (SQL Server)

  3. Jak połączyć wiele wierszy w listę rozdzielaną przecinkami w SQL Server 2005?

  4. Tablix:Powtarzanie wierszy nagłówka na każdej stronie nie działa — Report Builder 3.0

  5. Utwórz tabelę partycjonowaną w SQL Server (T-SQL)