2010-12-30 24 views
8

Estoy leyendo un archivo de hoja de cálculo a través de C# ... y mi hoja de cálculo tiene más de 1000 filas. Necesito enviar cada fila de datos al procedimiento almacenado para realizar alguna lógica del lado de la base de datos y actualizar los registros. Necesito ayuda para enviar todas esas 1000 filas de datos en un solo viaje de ida y vuelta para ahorrar tiempo. ¿Cuál es la técnica necesaria para agrupar todas estas 1000 filas de datos?Para pasar filas masivas de datos al procedimiento almacenado

+0

¿Está utilizando SQL Server? Si es así, ¿qué versión de SQL Server estás usando? –

+1

1000 no son tantos, ¿está seguro de que necesita molestarse? Es posible que encuentre 1000 filas de una en una, sin embargo, no presenta ningún problema de rendimiento notable. Si es más fácil hacerlo uno a la vez, probaría primero y vería qué pasa. –

Respuesta

20

Suponiendo que está utilizando SQL Server 2008 o mejor, tiene algunas opciones. Todas estas opciones fueron cubiertas en gran detalle en Tech-Ed 2010 en New Orleans y video of the session is available online. A continuación se encuentra un resumen de las opciones presentadas.

Opción # 1 - Inserción masiva (no realmente cubierto de video)

Esta es una gran opción si sólo tiene que "volcar" los datos en una tabla y que no es necesario hacer mucho con los datos, excepto obtenerlo en la base de datos. Esto también se admite en ADO.NET utilizando el objeto SqlBulkCopy. También he escrito un lightweight wrapper you can find on CodePlex para que trabajar con SQL Server y ADO.NET más fácil

Opción # 2 - Ir a una lista delimitada por

tomar todos sus datos y construir una gran cadena y pasar toda la cadena en un procedimiento almacenado. Esto es sorprendentemente rápido pero viene con mucho equipaje. Tienes que tener una función dividida para obtener los datos y para obtener el mejor rendimiento necesitarás dividirlos con SQL-CLR y eso puede ser sorprendente si no eres dueño de la base de datos.

Opción # 3 - Pasar como XML

Esto es casi idéntica a la opción # 2, ya que están pasando una vez más en una cadena gigante en un solo parámetro. Esto también tiene un rendimiento razonable, pero también viene con gran parte del mismo bagaje que tiene la Opción n. ° 2, pero sin la función de división porque el servidor Sql sabe cómo analizar XML.

Opción # 4 - Utilice una función de valor tabla (SQL Server 2008)

Esta opción es muy fresco y proporciona el mejor rendimiento. Comienza creando un tipo de valor en SQL Server de tipo "Tabla" y luego crea un procedimiento almacenado que toma ese tipo de valor como un parámetro. En C#, ahora puede crear un SqlCommand y agregar un parámetro con un tipo de SqlDbType.Structured.

cmd.CommandType = CommandType.StoredProcedure; 
cmd.CommandText = "Test.spTVP"; 
var p = cmd.Parameters.Add("@Values", SqlDbType.Structured); 
p.TypeName = "Test.OrderTableType"; 
p.Value = dataTable; 
cmd.Execute…; 

Cuando se ejecuta el procedimiento almacenado, todos los datos están disponibles en la variable de tabla del procedimiento almacenado. Se puede usar como cualquier otra variable de tabla, por lo que mover los datos es muy simple.

Opción # 5 - Utilizar un Streaming Tabla función de valor (SQL Server 2008)

Un poco más de trabajo a continuación, la opción # 4 porque hay que configurar un iterador, pero te dan un poco de rendimiento loca de porque no tiene que cargar todos los datos en el cliente antes de pasarlo al procedimiento almacenado. .NET Runtime realmente transmitirá los datos en la base de datos y la implementación del procedimiento almacenado es la misma.

class MyStreamingTvp : IEnumerable<SqlDataRecord> { … 
} 
… 
cmd.CommandType = CommandType.StoredProcedure; 
cmd.CommandText = "Test.spTVP"; 
var p = cmd.Parameters.Add("@Values", SqlDbType.Structured); 
p.TypeName = "Test.OrderTableType"; 
p.Value = new MyStreamingTvp(…); 
cmd.Execute…; 

Todas estas opciones se explica con gran detalle y un poco de humor en el video he mencionado al principio. Fue una de mis sesiones favoritas en Tech-Ed este año.

+0

+1: Muy buenas opciones, espero que pueda votar más – TalentTuner

+0

@Saurabh Lo hice por usted :-) –

+0

Hola Ryan, ¡muy buena respuesta! Solo el video Link ya no funciona.¿Puedes publicar un nuevo enlace o todavía sabes el título de la sesión? Los videos de la sesión deben estar todavía en cannel9 ... – Markus

3

La respuesta de Ryan es muy exhaustiva y cubre las diversas opciones. Para un número relativamente pequeño de filas (1000-5000 es bastante pequeño), usaría lo que se describe como la opción n. ° 3, pasando XML como un parámetro de procedimiento almacenado. Hacemos esto a menudo en mi tienda y el siguiente es el código asociado:

Supongo que los datos de su hoja de cálculo son simples y ya los tiene fácilmente disponibles dentro de su código como algo así como una Lista que ha creado o una DataTable. Para este simple ejemplo, supondré que sus datos son una DataTable por simplicidad.

que, al igual que Ryan, también estoy asumiendo SQL 2008.

1 - Preparar los datos en C# mediante la transformación de los datos en XML que serán pasados ​​al procedimiento almacenado. Esto es solo una cadena de XML. Usamos un método en nuestra clase de Datos Base. Usted transfiere su DataTable y lo convertirá en una cadena XML simple que puede pasar como parámetro de su procedimiento almacenado.

public string ConvertToXMLDataString(DataTable table) { 
     StringBuilder XMLString = new StringBuilder(); 
     if (string.IsNullOrEmpty(table.TableName)) 
      table.TableName = "DataTable"; 
     XMLString.AppendFormat("<{0}>", table.TableName); 
     DataColumnCollection tableColumns = table.Columns; 
     foreach (DataRow row in table.Rows) { 
      XMLString.AppendFormat("<RowData>"); 
      foreach (DataColumn column in tableColumns) { 
       XMLString.AppendFormat("<{1}>{0}</{1}>", row[column].ToString(), column.ColumnName); 
      } 
      XMLString.AppendFormat("</RowData>"); 
     } 
     XMLString.AppendFormat("</{0}>", table.TableName); 
     return XMLString.ToString(); 
    } 

2 - He creado un simple ejemplo DataTable que contendrá 1000 filas de datos, todos los números enteros, 10 columnas

DataTable table = new DataTable("DataTable"); 
      for(int i = 1; i < 11; i++){ 
       table.Columns.Add(new DataColumn("Column" + i.ToString())); 
      } 
      int j = 0; 
      for (int i = 0; i < 1000; i++) { 
       DataRow newRow = table.NewRow(); 
       for (int k = 0; k < table.Columns.Count; k++) { 
        newRow[k] = j++; 
       }    
       table.Rows.Add(newRow); 
      } 

El resultado final de pasar el DataTable a ConvertToXMLDataString es un buen formato representación XML de la DataTable que se puede pasar en el procedimiento almacenado y fácilmente selecciona entre:

<DataTable> 
    <RowData> 
     <Column1>0</Column1> 
     <Column2>1</Column2> 
     <Column3>2</Column3> 
     <Column4>3</Column4> 
     <Column5>4</Column5> 
     <Column6>5</Column6> 
     <Column7>6</Column7> 
     <Column8>7</Column8> 
     <Column9>8</Column9> 
     <Column10>9</Column10> 
    </RowData> 
    <RowData> 
     <Column1>10</Column1> 
     <Column2>11</Column2> 
     <Column3>12</Column3> 
     <Column4>13</Column4> 
     <Column5>14</Column5> 
     <Column6>15</Column6> 
     <Column7>16</Column7> 
     <Column8>17</Column8> 
     <Column9>18</Column9> 
     <Column10>19</Column10> 
    </RowData> 
</DataTable> 

3 - Ahora, crear un procedimiento almacenado que se encargará de que X Cadena de datos ML que se le ha pasado.

CREATE PROCEDURE [dbo].[pr_Test_ConvertTable] 
@TableData XML 
AS 
BEGIN 
    SET NOCOUNT ON 
    SET ANSI_NULLS ON 
    SET ARITHABORT ON 

    DECLARE @TempTable TABLE (
     Column1 int, Column2 int, Column3 int, Column4 int, Column5 int, 
     Column6 int, Column7 int, Column8 int, Column9 int, Column10 int 
    ) 

    INSERT INTO @TempTable (Column1, Column2, Column3, Column4, Column5, Column6, Column7, Column8, Column9, Column10) 
    SELECT XmlTable.Data.value('(./Column1)[1]','int'), XmlTable.Data.value('(./Column2)[1]','int'), 
    XmlTable.Data.value('(./Column3)[1]','int'), XmlTable.Data.value('(./Column4)[1]','int'), 
    XmlTable.Data.value('(./Column5)[1]','int'), XmlTable.Data.value('(./Column6)[1]','int'), 
    XmlTable.Data.value('(./Column7)[1]','int'), XmlTable.Data.value('(./Column8)[1]','int'), 
    XmlTable.Data.value('(./Column9)[1]','int'), XmlTable.Data.value('(./Column10)[1]','int') 
    FROM @TableData.nodes('//DataTable/RowData') AS XmlTable(Data) 

    SELECT * FROM @TempTable 
END 
GO 

4 - El procedimiento acepta la variable XML de @TableData y lo inserta en una variable de tabla de nueva creación denominada @TempTable.

El paso final es crear ahora la llamada a su base de datos con el parámetro XML adecuado. Llame al SP como lo haría normalmente, solo use esto como el parámetro.

cmd.Parameters.Add("@TableData", SqlDbType.Xml).Value = ConvertToXMLDataString(table); 

Lo tienes. Debería poder ajustar en consecuencia para manejar sus datos. Por lo general, no me gusta pasar DataTables, preferiría pasar un Objeto o una Lista, pero en esta situación, probablemente ya tengas tus datos en el DataTable.

Si esto es algo que se hace una sola vez, o algo que no sucede con frecuencia, el rendimiento que usted obtiene a cambio de la conveniencia de usar XML es mínimo. Si esto es algo que sucede a menudo por muchos usuarios, use el enfoque más eficiente.

+0

Oh, y nunca lo hago SELECCIONAR *, eso es cojo. Solo para el ejemplo. No a esto. –

Cuestiones relacionadas