sql >> Base de Datos >  >> RDS >> Sqlserver

El método más rápido para inserciones, actualizaciones y selecciones de SQL Server

Esta respuesta se centra principalmente en las operaciones 'seleccionar' frente a actualizar/crear/eliminar. Creo que es más raro actualizar más de uno o unos pocos registros a la vez, por lo que también creo que 'seleccionar' es donde tienden a ocurrir los cuellos de botella. Dicho esto, necesita conocer su aplicación (perfil). El mejor lugar para enfocar su tiempo de optimización es casi siempre en el nivel de la base de datos en las propias consultas, en lugar del código del cliente. El código del cliente es solo la plomería:no es la fuerza principal de su aplicación. Sin embargo, dado que la plomería tiende a reutilizarse en muchas aplicaciones diferentes, simpatizo con el deseo de que sea lo más óptima posible y, por lo tanto, tengo mucho que decir sobre cómo construir ese código.

Tengo un método genérico para seleccionar consultas/procedimientos en mi capa de datos que se parece a esto:

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

Y eso me permite escribir métodos de capa de datos públicos que usan métodos anónimos para agregar los parámetros. El código que se muestra funciona con .Net 2.0+, pero se puede escribir aún más corto usando .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;
       }
     );
}

Me detendré aquí para poder indicarle nuevamente el código que se encuentra arriba y que usa el método anónimo para la creación de parámetros.

Este es un código muy limpio, en el sentido de que coloca la definición de consulta y la creación de parámetros en el mismo lugar y, al mismo tiempo, le permite abstraer la conexión de la base de datos repetitiva/código de llamada a un lugar más reutilizable. No creo que esta técnica esté cubierta por ninguno de los puntos de su pregunta, y también es bastante rápido. Creo que esto cubre la esencia de tu pregunta.

Sin embargo, quiero continuar explicando cómo encaja todo esto. El resto es bastante sencillo, pero también es fácil arrojar esto a una lista o similar y hacer las cosas mal, lo que finalmente perjudica el rendimiento. A continuación, la capa empresarial utiliza una fábrica para traducir los resultados de la consulta a objetos (c# 3.0 o posterior):

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

En lugar de tener estos en vivo en su clase, también puede agruparlos todos juntos en una clase estática diseñada específicamente para contener los métodos de fábrica.

Necesito hacer un cambio en el método de recuperación original. Ese método "produce" el mismo objeto una y otra vez, y esto no siempre funciona tan bien. Lo que queremos hacer diferente para que funcione es forzar una copia del objeto representado por el registro actual, de modo que cuando el lector cambie para el siguiente registro estemos trabajando con datos limpios. Esperé hasta después de mostrar el método de fábrica para que podamos usarlo en el código final. El nuevo método Recuperar se ve así:

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

Y ahora llamaríamos a ese nuevo método Retrieve() así:

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

Obviamente, este último método se puede ampliar para incluir cualquier lógica comercial adicional necesaria. También resulta que este código es excepcionalmente rápido, porque aprovecha las funciones de evaluación diferida de IEnumerable. La desventaja es que tiende a crear una gran cantidad de objetos de corta duración, y eso puede perjudicar el rendimiento transaccional sobre el que preguntó. Para evitar esto, a veces rompo un buen nivel n y paso los objetos IDataRecord directamente al nivel de presentación y evito la creación innecesaria de objetos para registros que simplemente están vinculados a un control de cuadrícula de inmediato.

Actualizar/Crear código es similar, con la diferencia de que, por lo general, solo cambia un registro a la vez en lugar de muchos.

O podría ahorrarte la lectura de esta larga publicación y simplemente decirte que uses Entity Framework;)