2012-03-30 8 views
9

Estoy tratando de utilizar Dapper para admitir mi acceso a datos para mi aplicación de servidor.Método correcto para eliminar más de 2100 filas (por ID) con Dapper

Mi aplicación de servidor tiene otra aplicación que coloca registros en mi base de datos a una velocidad de 400 por minuto.

Mi aplicación los saca en lotes, los procesa y luego los elimina de la base de datos.

Como los datos continúan fluyendo en la base de datos mientras estoy procesando, no tengo una buena manera de decir delete from myTable where allProcessed = true.

Sin embargo, sé el valor PK de las filas para eliminar. Así que quiero hacer un delete from myTable where Id in @listToDelete

El problema es que si mi servidor cae por 6 minutos, entonces tengo más de 2100 filas para eliminar.

Dado que Dapper toma mi @listToDelete y convierte cada una en un parámetro, mi llamada para eliminar falla. (Causando que mi depuración de datos se retrase aún más.)

¿Cuál es la mejor manera de lidiar con esto en Dapper?

NOTAS: He examinado los parámetros de tabla de valores, pero por lo que puedo ver, no son muy performant. Esta pieza de mi arquitectura es el cuello de botella de mi sistema y necesito ser muy muy rápido.

+0

@marc_s - No ** necesito ** para pasar esos muchos parámetros ... Pero sí tengo que eliminar tantas filas por PK Id. Sin embargo, yo hago eso está bien por mí. Le digo a Dapper que elimine todas las filas de mi '@ list'. Es Dapper que está haciendo los parámetros de cada elemento en mi lista. – Vaccano

+0

Difícil de decir a partir de esta pequeña información, pero ¿por qué no puede utilizar los criterios de selección para el lote como sus criterios para una eliminación. O tener un indicador procesado en MyTable, configurarlo en el "procesamiento" y luego usarlo. No es brillante, pero será mucho más rápido que eliminarlos uno por uno. No va a ser terriblemente malo incluso con 10.000 registros. –

+0

Mis criterios de lote no son muy efectivos. Entonces preferiría no usar eso. Podría agregar un indicador procesado, pero para hacer eso necesitaría una forma de llamar las filas para agregar el indicador procesado. Si puedo llamarlos, puedo borrarlos. (Podría agregar una bandera para decir "batched" en el horario seleccionado. Pero preferiría no hacerlo) – Vaccano

Respuesta

12

Una opción es crear una tabla temporal en el servidor y luego usar la función de carga masiva para cargar todos los ID en esa tabla a la vez. A continuación, utilice una cláusula join, EXISTS o IN para eliminar solo los registros que cargó en su tabla temporal.

Las cargas masivas son una ruta bien optimizada en SQL Server y deberían ser muy rápidas.

Por ejemplo:

  1. Ejecuta la sentencia CREATE TABLE #RowsToDelete(ID INT PRIMARY KEY)
  2. utilice una carga mayor para insertar llaves en #RowsToDelete
  3. Ejecutar DELETE FROM myTable where Id IN (SELECT ID FROM #RowsToDelete)
  4. Ejecutar DROP TABLE #RowsToDelte (la tabla también será dado de baja automáticamente si se cierra la sesión)

(Asumiendo Dapper) ejemplo de código:

conn.Open(); 

var columnName = "ID"; 

conn.Execute(string.Format("CREATE TABLE #{0}s({0} INT PRIMARY KEY)", columnName)); 

using (var bulkCopy = new SqlBulkCopy(conn)) 
{ 
    bulkCopy.BatchSize = ids.Count; 
    bulkCopy.DestinationTableName = string.Format("#{0}s", columnName); 

    var table = new DataTable();      
    table.Columns.Add(columnName, typeof (int)); 
    bulkCopy.ColumnMappings.Add(columnName, columnName); 

    foreach (var id in ids) 
    { 
     table.Rows.Add(id); 
    } 

    bulkCopy.WriteToServer(table); 
} 

//or do other things with your table instead of deleting here 
conn.Execute(string.Format(@"DELETE FROM myTable where Id IN 
            (SELECT {0} FROM #{0}s", columnName)); 

conn.Execute(string.Format("DROP TABLE #{0}s", columnName)); 
+0

Código de ejemplo agregado que realmente usa SqlBulkCopy, podría ser adaptado para tablas más amplias agregando más columnas a la temperatura tabla y tabla de datos. –

5

Para que funcione este código, me fui del lado oscuro.

Dado que Dapper hace mi lista en los parámetros. Y SQL Server no puede manejar una gran cantidad de parámetros. (Nunca antes he necesitado parámetros de doble dígito). Tenía que ir con Dynamic SQL.

Así que aquí fue mi solución:

string listOfIdsJoined = "("+String.Join(",", listOfIds.ToArray())+")"; 
connection.Execute("delete from myTable where Id in " + listOfIdsJoined); 

Antes de que todos los agarra sus antorchas y horcas, vamos a explicar.

  • Este código se ejecuta en un servidor cuya única entrada es una fuente de datos de un sistema Mainframe.
  • La lista que estoy creando dinámicamente es una lista de longs/bigints.
  • Los largos/grandes son de una columna de Identidad.

Sé que la construcción de SQL dinámico es mala juju, pero en este caso, simplemente no puedo ver cómo esto lleva a un riesgo de seguridad.

+2

[Little Bobby Tables] (https://xkcd.com/327/) say's G'Day! –

+3

@ Pure.Krome presumiblemente su 'listOfIds' es del tipo' List 'por lo tanto, no es necesario desinfectar las entradas – Zac

+0

Probablemente ... pero las suposiciones son la raíz de todo mal. En segundo lugar, es la sugerencia general => crear un script sql, sobre la marcha y en una cadena ... podría terminar muy mal :) –

2

solicitud Dapper la Lista de objeto que tiene parámetro como una propiedad por lo que en caso anterior una lista de objeto que tiene Id como propiedad funcionará.

connection.Execute("delete from myTable where Id in (@Id)", listOfIds.AsEnumerable().Select(i=> new { Id = i }).ToList()); 

Esto funcionará.

Cuestiones relacionadas