2012-01-29 12 views
6

Mam zdalne połączenie sql w języku C#, które musi wykonać kwerendę i zapisać wyniki na lokalnym dysku twardym użytkownika. Istnieje dość duża ilość danych, które mogą zostać zwrócone, więc trzeba pomyśleć o skutecznym sposobie jej przechowywania. Czytałem wcześniej, zanim po raz pierwszy zapisałem cały wynik w pamięci, a następnie napisanie go nie jest dobrym pomysłem, więc jeśli ktoś mógłby pomóc, byłoby wspaniale!Jak skutecznie pisać do pliku z SQL datareader w języku C#?

Jestem obecnie przechowywania danych wynikowych sql do DataTable, chociaż mam na myśli to może być robić coś w while(myReader.Read(){...} Poniżej lepiej jest kod, który pobiera wyniki:

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

Więc teraz mam to ogromny ciąg wyników. I mam datatable. Musi być o wiele lepszy sposób na zrobienie tego?

Dzięki.

+0

Możliwy duplikat http://stackoverflow.com/questions/2244655/how-to-serialize-a-datatable-to-a-string –

+0

Czy piszesz tylko do niesformatowanego pliku płaskiego, czy byłoby lepiej umieścić dane w kolumnach, takich jak arkusz kalkulacyjny .csv? – DOK

+0

płaski plik comma-del. – Sam

Odpowiedz

18

Jesteś na dobrej drodze. Użyj pętli z while(myReader.Read(){...} i zapisz każdy rekord do pliku tekstowego wewnątrz pętli. Platforma .NET i system operacyjny zajmą się efektywnym przepłukiwaniem buforów na dysk.

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

Dzięki. jakiej metody pisania powinienem użyć? streamwriter? IO? przykład byłby bardzo pomocny. dzięki – Sam

+0

Użyj StreamWriter, jeśli chcesz pisać do pliku. Zobacz moją aktualizację na przykładzie. –

+0

Dzięki. Dla wszystkich wierszy używam: 'StringBuilder row = new StringBuilder(); dla (int i = 0; i Sam

2

Zgadzam się, że najlepiej będzie tutaj użyć SqlDataReader. Coś takiego:

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() + ","); 

     } 
} 

Pamiętaj, w pętli while można napisać ten wiersz do pliku tekstowego w dowolnym formacie można zobaczyć pasuje do danych kolumn z SqlDataReader.

1

wpadłem na to, że to lepsze niż pisarz CSV innych odpowiedzi:

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

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

+1

Nie chcesz '- 1' na pętli' for (int index = 0; index vapcguy

+1

@vacguy, dzięki, że poprawiłem swoją odpowiedź –

2

Rob Sedgwick odpowiedź jest bardziej lubić to, ale można go ulepszyć i uprościć. To jak to zrobiłem:

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

i trzeba mieć GetColumnNames funkcyjne:

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

użyciu obiektu reakcji bez response.Close() przyczyn przynajmniej w niektórych przypadkach kod HTML na piśmie strony z dane do zapisania w pliku. Jeśli używasz Response.Close(), połączenie może zostać zamknięte przedwcześnie i spowodować błąd podczas tworzenia pliku.

Zaleca się używanie HttpApplication.CompleteRequest(), ale wydaje się, że zawsze powoduje to, że html zostanie zapisany na końcu pliku.

Próbowałem wypróbować strumień w połączeniu z obiektem odpowiedzi i odniosłem sukces w środowisku programistycznym. Nie próbowałem go jeszcze w produkcji.

2

Utrzymanie oryginalne podejście, tutaj jest szybkie zwycięstwo:

Zamiast String jako tymczasowy bufor, użyj StringBuilder. Umożliwi to korzystanie z funkcji .append(String) w przypadku konkatenacji, zamiast korzystania z operatora +=.

Operator += jest szczególnie nieefektywny, więc jeśli umieścisz go w pętli i zostanie powtórzony (potencjalnie) miliony razy, wpłynie to na wydajność.

Sposób .append(String) nie zniszczy oryginalnego obiektu, więc jest to szybsze

1

użyłem .CSV do eksportowania danych z bazyprzez DataReader. w moim projekcie czytam datareader i ręcznie tworzę plik .CSV. w pętli czytam datareader i dla każdego wiersza dołączam wartość komórki do łańcucha wynikowego. dla oddzielnych kolumn używam "," i dla oddzielnych wierszy używam "\ n". ostatecznie zapisałem ciąg wyników jako result.csv.

Proponuję to high performance extension. testowałem i szybko eksportowałem 600 000 wierszy jako .CSV.

+0

Podczas gdy ten link może odpowiedzieć na pytanie, lepiej jest umieścić tutaj istotne części odpowiedzi i podać link do odniesienia. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni. - [Z recenzji] (/ opinia/niskiej jakości-posts/16209261) –

+0

Dziękuję za komentarz. Ukończyłem moją odpowiedź – Nigje

Powiązane problemy