Нужно создать пакет данных большого размера и записать каждый пакет в текстовый файл - VB.Net - PullRequest
0 голосов
/ 24 апреля 2020

У меня есть требование, что мне нужно запросить БД и получить записи в таблице данных. Таблица данных содержит 20 000 записей. Мне нужно пакетировать эти записи в пакетах по 100 записей в каждой и записывать эти пакеты в отдельные текстовые файлы.

До сих пор я был в состоянии пакетировать записи в пакетах по 100 каждая, используя IEnumerable (из DataRow).

Сейчас я сталкиваюсь с проблемой при записи IEnumeable (Of DatRow) в текстовый файл.

Мой код ниже:

Dim strsql = "Select * from myTable;"
Dim dt as DataTable
Using cnn as new SqlConnection(connectionString)
cnn.Open()
Using dad as new SqlAdapter(strsql ,cnn)
dad.fill(dt)
End Using
cnn.Close()
End Using 

Dim Chunk = getChunks(dt,100)
For each chunk as IEnumerable(Of DataRow) In Chunks
Dim path as String = "myFilePath"
If Not File.Exists(myFilePath) Then
  //** Here I will write my Batch into the File.
End If
Next

Public Iterator Function getChunks(byVal Tab as DataTable, byVal size as Integer) as IEnumerable (Of IEnumerable(of DataRow))

Dim chunk as List(Of DataRow) = New List(of DataRow)(size)
For Each row As DataRow in tab.Rows
chunk.Add(row)
if chunk.Count = size Then
Yield chunk
chunk = New List(of DataRow0(size)
Next
if chunk.Any() Then Yield chunk

End Function

Нужна ваша помощь, чтобы написать IEneumerable DataRows в текстовый файл для каждой партии записей.

Спасибо

:)

Ответы [ 3 ]

1 голос
/ 24 апреля 2020

Ваш существующий код слишком сложен. Если это все, что вы делаете, то использование таблицы данных не нужно / неразумно; это один из немногих случаев, когда я бы рекомендовал использовать низкоуровневый считыватель данных для поддержания низкого уровня воздействия на память

Запись таблицы в файл в файл, быстрое, простое и низкое потребление памяти:

Dim dr = sqlCommand.ExecuteReader()

Dim sb as New StringBuilder
Dim lineNum = -1
Dim batchSize = 100

While dr.Read()

  'turn the row into a string for our file
  For x = 0 to dr.FieldCount -1
    sb.Append(dr.GetString(x)).Append(",")
  Next x
  sb.Length -= 1 'remove trailing comma
  sb.AppendLine()

  'keep track of lines written so we can batch accordingly 
  lineNum += 1
  Dim fileNum = lineNum \ batchSize
  File.AppendAllText($"c:\temp\file{fileNum}.csv", sb.ToString())

  'clear the stringbuilder 
  sb.Length = 0

End While

Если вы действительно хотите использовать таблицу данных, ничто не мешает вам поменять это while dr на For Each r as DataRow in myDatatable.Rows

Обратите внимание, что это не упражнение в создании полностью экранированного CSV, ни форматирование данных; он демонстрирует концепцию наличия пожарного шланга данных и простой записи его в N различных файлов, используя тот факт, что деление целого числа на каждое число от 0 до 99 приведет к 0 (и, следовательно, go в файле 0) и тогда само число от 1 до 199 приведет к 1 (и, следовательно, к строкам go в файле 1) et c и выполнению этого процесса в одном потоке данных или в одной итерации из N элементов

Вы можете построить строки файла в конструкторе строк и записывать их каждый batchSize if lineNum Mod batchSize = batchSize - 1, если вы чувствуете, что это будет более эффективно, чем вызов файла appendalltext (который открывает и закрывает файл)

0 голосов
/ 24 апреля 2020

Я думал, что ваш код отлично использовал функцию итерации.

Вот код вашего итератора.

Public Iterator Function getChunks(ByVal Tab As DataTable, ByVal size As Integer) As IEnumerable(Of IEnumerable(Of DataRow))
    Dim chunk As List(Of DataRow) = New List(Of DataRow)(size)
    For Each row As DataRow In Tab.Rows
        chunk.Add(row)
        If chunk.Count = size Then
            Yield chunk
            chunk = New List(Of DataRow)(size)
        End If
    Next
    If chunk.Any() Then Yield chunk
End Function

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim dt = LoadDataTable()
    Dim myFilePath As String = "C:\Users\xxx\Documents\TestLoop\DataFile"
    Dim FileNum = 1
    For Each chunk As IEnumerable(Of DataRow) In getChunks(dt, 100)
        For Each row As DataRow In chunk
            Dim s = String.Join("|", row.ItemArray)
            File.AppendAllText(myFilePath & FileNum & ".txt", s & Environment.NewLine)
        Next
        FileNum += 1
    Next
    MessageBox.Show("Done")
End Sub

Вам просто нужно было вложить For Each, чтобы получить строки данных.

0 голосов
/ 24 апреля 2020

Протестировано это с таблицей чуть более 1500 записей и 10 полей. Создание файла заняло чуть более 5 секунд (без учета доступа к данным). При прочих равных условиях (что я знаю, что это не так), это заняло бы 13 секунд при записи файлов.

Поскольку ваша проблема была с итератором, я предполагаю, что с DataTable. * Не было проблем с памятью. 1004 *

Вы можете включить более одного объекта базы данных в блок Using, используя запятую для обозначения списка объектов в Using.

Private Sub OPCode()
    Dim myFilePath = "C:\Users\xxx\Documents\TestLoop\DataFile"

    Dim strsql = "Select * from myTable;"
    Dim dt As New DataTable
    Using cnn As New SqlConnection(connectionString),
            cmd As New SqlCommand(strsql, cnn)
        cnn.Open()
        dt.Load(cmd.ExecuteReader)
    End Using
    sw.Start()
    Dim StartRow = 0
    Dim EndRow = 99
    Dim FileNum = 1
    Dim TopIndex = dt.Rows.Count - 1
    Do
        For i = StartRow To EndRow
            Dim s = String.Join("|", dt.Rows(i).ItemArray)
            File.AppendAllText(myFilePath & FileNum & ".txt", s & Environment.NewLine)
        Next
        FileNum += 1
        StartRow += 100
        EndRow += 100
        If EndRow >= TopIndex Then
            EndRow = TopIndex
        End If
    Loop Until StartRow >= TopIndex
    sw.Stop()
    MessageBox.Show(sw.ElapsedMilliseconds.ToString)
End Sub
.
...