2012-01-29 21 views
6

Tengo una conexión sql remota en C# que necesita ejecutar una consulta y guardar sus resultados en el disco duro local de los usuarios. Hay una cantidad bastante grande de datos que esto puede devolver, por lo que es necesario pensar en una forma eficiente de almacenarlo. He leído antes que primero poniendo todo el resultado en la memoria y luego escribirlo no es una buena idea, así que si alguien pudiera ayudar, ¡sería genial!¿Cómo escribir eficientemente en el archivo desde el lector de datos SQL en C#?

estoy almacenando los datos del resultado de SQL en un DataTable, aunque estoy pensando que podría ser mejor hacer algo en while(myReader.Read(){...} A continuación se muestra el código que obtiene los resultados:

  DataTable t = new DataTable(); 
      string myQuery = QueryLoader.ReadQueryFromFileWithBdateEdate(@"Resources\qrs\qryssysblo.q", newdate, newdate); 
      using (SqlDataAdapter a = new SqlDataAdapter(myQuery, sqlconn.myConnection)) 
      { 
       a.Fill(t); 
      } 

      var result = string.Empty; 
    for(int i = 0; i < t.Rows.Count; i++) 
    { 
     for (int j = 0; j < t.Columns.Count; j++) 
     { 
      result += t.Rows[i][j] + ","; 
     } 


     result += "\r\n"; 
    } 

Así que ahora tengo este gran cadena de resultados. Y tengo la tabla de datos. Tiene que haber una forma mucho mejor de hacerlo?

Gracias.

+0

Posible duplicado de http://stackoverflow.com/questions/2244655/how-to-serialize-a-datatable-to-a-string –

+0

¿Estás escribiendo en un archivo plano sin formato, o sería mejor que poner los datos en columnas como una hoja de cálculo .csv? – DOK

+0

plano comma-del archivo. – Sam

Respuesta

18

Estás en el camino correcto. Use un bucle con while(myReader.Read(){...} y escriba cada registro en el archivo de texto dentro del bucle. .NET framework y el sistema operativo se encargarán de enjuagar los búferes en el disco de una manera eficiente.

using(SqlConnection conn = new SqlConnection(connectionString)) 
using(SqlCommand cmd = conn.CreateCommand()) 
{ 
    conn.Open(); 
    cmd.CommandText = QueryLoader.ReadQueryFromFileWithBdateEdate(
    @"Resources\qrs\qryssysblo.q", newdate, newdate); 

    using(SqlDataReader reader = cmd.ExecuteReader()) 
    using(StreamWriter writer = new StreamWriter("c:\temp\file.txt")) 
    { 
    while(reader.Read()) 
    { 
     // Using Name and Phone as example columns. 
     writer.WriteLine("Name: {0}, Phone : {1}", 
     reader["Name"], reader["Phone"]); 
    } 
    } 
} 
+0

Gracias. ¿Qué método de escritura debo usar? Streamwriter? IO? un ejemplo sería muy útil. gracias – Sam

+0

Use un StreamWriter si desea escribir en un archivo. Ver mi actualización con un ejemplo. –

+0

Gracias. Para todas las filas que estoy usando: 'StringBuilder row = new StringBuilder(); para (int i = 0; i Sam

2

Estoy de acuerdo que la mejor opción en este caso sería utilizar un SqlDataReader. Algo como esto:

StreamWriter YourWriter = new StreamWriter(@"c:\testfile.txt"); 
SqlCommand YourCommand = new SqlCommand(); 
SqlConnection YourConnection = new SqlConnection(YourConnectionString); 
YourCommand.Connection = YourConnection; 
YourCommand.CommandText = myQuery; 

YourConnection.Open(); 

using (YourConnection) 
{ 
    using (SqlDataReader sdr = YourCommand.ExecuteReader()) 
     using (YourWriter) 
     { 
      while (sdr.Read()) 
       YourWriter.WriteLine(sdr[0].ToString() + sdr[1].ToString() + ","); 

     } 
} 

Eso sí, en el bucle while, puede escribir esa línea en el fichero de texto en cualquier formato que convenga con los datos de la columna de la SqlDataReader.

1

me ocurrió con esto, es un mejor escritor CSV que las otras respuestas:

public static class DataReaderExtension 
{ 
    public static void ToCsv(this IDataReader dataReader, string fileName, bool includeHeaderAsFirstRow) 
    { 

     const string Separator = ","; 

     StreamWriter streamWriter = new StreamWriter(fileName); 

     StringBuilder sb = null; 

     if (includeHeaderAsFirstRow) 
     { 
      sb = new StringBuilder(); 
      for (int index = 0; index < dataReader.FieldCount; index++) 
      { 
       if (dataReader.GetName(index) != null) 
        sb.Append(dataReader.GetName(index)); 

       if (index < dataReader.FieldCount - 1) 
        sb.Append(Separator); 
      } 
      streamWriter.WriteLine(sb.ToString()); 
     } 

     while (dataReader.Read()) 
     { 
      sb = new StringBuilder(); 
      for (int index = 0; index < dataReader.FieldCount; index++) 
      { 
       if (!dataReader.IsDBNull(index)) 
       { 
        string value = dataReader.GetValue(index).ToString(); 
        if (dataReader.GetFieldType(index) == typeof(String)) 
        { 
         if (value.IndexOf("\"") >= 0) 
          value = value.Replace("\"", "\"\""); 

         if (value.IndexOf(Separator) >= 0) 
          value = "\"" + value + "\""; 
        } 
        sb.Append(value); 
       } 

       if (index < dataReader.FieldCount - 1) 
        sb.Append(Separator); 
      } 

      if (!dataReader.IsDBNull(dataReader.FieldCount - 1)) 
       sb.Append(dataReader.GetValue(dataReader.FieldCount - 1).ToString().Replace(Separator, " ")); 

      streamWriter.WriteLine(sb.ToString()); 
     } 
     dataReader.Close(); 
     streamWriter.Close(); 
    } 
} 

uso: mydataReader.ToCsv ("myfile.csv", true)

+1

No desea el '- 1' en el bucle' for (int index = 0; index vapcguy

+1

@vacguy Gracias, he corregido mi respuesta –

2

Rob Sedgwick respuesta es más me gusta, pero se puede mejorar y simplificar. Esto es cómo lo hice:

string separator = ";"; 
string fieldDelimiter = ""; 
bool useHeaders = true; 

string connectionString = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 

using (SqlConnection conn = new SqlConnection(connectionString)) 
{ 
    using (SqlCommand cmd = conn.CreateCommand()) 
    { 
      conn.Open(); 
      string query = @"SELECT whatever"; 

      cmd.CommandText = query; 

      using (SqlDataReader reader = cmd.ExecuteReader()) 
      { 
       if (!reader.Read()) 
       { 
        return; 
       } 

       List<string> columnNames = GetColumnNames(reader); 

       // Write headers if required 
       if (useHeaders) 
       { 
        first = true; 
        foreach (string columnName in columnNames) 
        { 
          response.Write(first ? string.Empty : separator); 
          line = string.Format("{0}{1}{2}", fieldDelimiter, columnName, fieldDelimiter); 
          response.Write(line); 
          first = false; 
        } 

        response.Write("\n"); 
       } 

       // Write all records 
       do 
       { 
        first = true; 
        foreach (string columnName in columnNames) 
        { 
          response.Write(first ? string.Empty : separator); 
          string value = reader[columnName] == null ? string.Empty : reader[columnName].ToString(); 
          line = string.Format("{0}{1}{2}", fieldDelimiter, value, fieldDelimiter); 
          response.Write(line); 
          first = false; 
        } 

        response.Write("\n"); 
       } 
       while (reader.Read()); 
      } 
    } 
} 

y que necesita para tener una función GetColumnNames:

List<string> GetColumnNames(IDataReader reader) 
{ 
    List<string> columnNames = new List<string>(); 
    for (int i = 0; i < reader.FieldCount; i++) 
    { 
     columnNames.Add(reader.GetName(i)); 
    } 

    return columnNames; 
} 
1

utilizando el objeto de respuesta sin un response.Close() causas por lo menos en algunos casos el HTML de la escritura página impresa los datos que se escribirán en el archivo. Si usa Response.Close(), la conexión puede cerrarse prematuramente y causar un error al producir el archivo.

Se recomienda utilizar el HttpApplication.CompleteRequest(), sin embargo, esto parece causar siempre que el html se escriba al final del archivo.

He intentado la secuencia junto con el objeto de respuesta y he tenido éxito en el entorno de desarrollo. No lo he probado en producción todavía.

2

Mantener su enfoque original, que aquí hay una rápida victoria:

En lugar de utilizar String como un buffer temporal, utilice StringBuilder. Eso le permitirá usar la función .append(String) para concatenaciones, en lugar de usar el operador +=.

El operador += es especialmente ineficiente, por lo que si lo coloca en un bucle y se repite (potencialmente) millones de veces, el rendimiento se verá afectado.

El método .append(String) no destruirá el objeto original, por lo que es más rápido

1

que utilizan .CSV para exportar datos desde la base de datos por DataReader. en mi proyecto, leí el lector de datos y creé el archivo .CSV a mano. en un bucle, leí el lector de datos y para cada una de las filas agregué el valor de la celda a la cadena resultante. para columnas separadas utilizo "," y para filas separadas utilizo "\ n". finalmente guardé la cadena de resultados como result.csv.

Sugiero este high performance extension. Lo probé y exporté rápidamente 600,000 filas como .CSV.

+0

Mientras que este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. - [De la crítica] (/ review/low-quality-posts/16209261) –

+0

Gracias por su comentario. Completé mi respuesta – Nigje

Cuestiones relacionadas