序列或批处理项DataGridView

时间:2016-07-26 18:09:54

标签: .net vb.net datagridview

我有大量的DataGridView,有940000行... ouch,通过解析csv文件填充,DataGridView有一个名为Sequence的列编号为1到940000.我试图做的是重新编号序列以便溢出对于DataGridView中的行数,为1到7000的序列。什么是重新排序序列列的最有效方法?

Using reader As New Microsoft.VisualBasic.FileIO.TextFieldParser(fileName)
        reader.TextFieldType = FileIO.FieldType.Delimited
        reader.SetDelimiters(",")
        Dim currentRow As String()
        Dim serial As String
        Dim sequence As Integer = 0
        Dim RollId As String

        'pbUploadFile.Maximum = serialAmmount / quantityBreak
        pbUploadFile.Maximum = serialAmmount
        pbUploadFile.Step = 1
        pbUploadFile.Value = 0

        For i = 1 To serialAmmount / quantityBreak
            For j = 1 To quantityBreak
                Try
                    currentRow = reader.ReadFields()
                    serial = currentRow(0).ToString
                    sequence += 1
                    EnterDataIntoDatabase(serial, sequence, nextRollNumber, ddSelectPartNumber.Text)
                    pbUploadFile.Increment(1)
                Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
                    MsgBox("Code " & ex.Message & "is not valid and will be skipped check csv file")
                End Try
            Next j

            sqlCmd = New SqlClient.SqlCommand("SELECT * FROM serials WHERE Sequence=@sequence AND RollNo=@rollNo ", sqlCon)
            sqlCmd.CommandType = CommandType.Text
            sqlCmd.Parameters.AddWithValue("@sequence", 1)
            sqlCmd.Parameters.AddWithValue("@rollNo", nextRollNumber)
            sqlCon.Open()
            Dim readRollId As SqlClient.SqlDataReader = sqlCmd.ExecuteReader()
            If readRollId.Read() Then
                RollId = readRollId.Item("Code")
            End If
            sqlCon.Close()


            UpdateAvailableRolls(ddSelectPartNumber.Text, nextRollNumber, RollId)
            nextRollNumber += 1
            UpdateRollNo(nextRollNumber)
            sequence = 0
            'pbUploadFile.Increment(1)
        Next i
        SaveFile()
    End Using

1 个答案:

答案 0 :(得分:2)

通常最好考虑如何使用数据以及何时确定如何做某事以及使用哪些工具来执行此操作。没人权利快|做大多数事情的有效方法。

那就是说,有一些糟糕的做事方式。使用DataGridView作为数据容器似乎不明智(我实际上无法在代码中看到与DGV相关的任何内容)。 A)没有自动的方式让数据进入它 - 你必须编写代码来执行它,2)没有自动的方式让数据去其他地方 - 你必须编写代码来循环它和钓鱼数据退出。然后,所有数据都可能存储为字符串。

除了批量处理项目外,还有更多的事情要发生。以下将从CSV导入行,处理它们并将它们写回数据库(我使用的是MySql,但概念是相同的)。

首先,TextFieldParser是一个非常方便的工具,但它有一个主要的缺点,它只返回字符串。如果CSV中包含价格,日期,布尔值等,则该类型将丢失。在许多情况下,CSVHelper将是更好的选择。

在这种情况下,由于数据的目的地是数据库,我会使用OleDB将CSV读入DataTable,批量处理,然后将其发送到数据库。

使用OleDB导入数据

<强> SCHEMA.INI

OleDb包含一个文本文件驱动程序,可用于解析CSV。它可以根据前几行的上下文“猜测”数据类型,但您也可以定义它们。在CSV所在的文件夹/目录中,创建一个名为Schema.INI的新文本文件。像这样定义CSV和列:

  

[Capitals.Csv]
  ColNameHeader =真
  格式= CSVDelimited
  TextDelimiter =
  DecimalSymbol =。
  CURRENCYSYMBOL = $
  Col1 =“国家”文字宽度254
  Col2 =“首都”文字宽度254
  Col3 =“人口”单一   Col4 =“Rank”整数
  Col5 =“国庆日”日期

  • 您可以在一个文件中包含多个csv定义,每个定义都以[...]
  • 开头
  • [...]将是CSV的名称
  • 如果CSV具有标题行,则可以将其用于列名称
  • 如果列也用引号括起来(“喜欢这个”,“在”中,“csv”),请使用TextDelimiter="
  • 每个Col#=条目定义数据类型并可以覆盖名称。这允许您将CSV中名为“Foo”的列“映射”到数据库中名为“Bar”的列。
  • 可以指定其他选项,如小数和货币符号以及文件中使用的代码页。

连接字符串

要使用的连接字符串是:

ACEImportStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='{0}';Extended Properties='TEXT'"

Data Source将是CSV和Schema.INI都存在的文件夹,'TEXT'元素告诉它使用Text驱动程序。使用文件夹名称填写空白:

ACEImportConnStr = String.Format(ACEImportConnStr, "C:\Temp")

OLEDB.12有时可能很挑剔,如果遇到问题,请改为使用Microsoft.Jet.OLEDB.4.0

现在,要加载数据,只需从CSV文件名(无文件夹)中选择:

Dim sSQL = "SELECT * FROM RandomOle.CSV"
...
Dim daSrc = New OleDbDataAdapter(sSQL, OleCSVConnstr)
rowsLoaded = daSrc.Fill(dtSample)

DataAdapter将读取定义的模式,并在几秒钟内将CSV加载到数据表中。处理其他任务还有很多工作要做,但这就是概念。

Dim sSQL = "SELECT * FROM YOUR_CSVFILE_NAME.CSV"
Dim sw As New Stopwatch

Dim rowsLoaded As Int32
Dim rowsUpdated As Int32

sw.Start()
ACEImportConnStr = String.Format(ACEImportConnStr, "C:\Temp")

' create Destination MySQL conn, Src and Dest dataadapters,
' and a command builder (because I am lazy...and fallible)
Using mysqlCon As New MySqlConnection(MySQLConnStr),
    daSrc As New OleDbDataAdapter(sSQL, ACEImportConnStr),
    daDest As New MySqlDataAdapter("SELECT * FROM Sample", mysqlCon),
    cb As New MySqlCommandBuilder(daDest)

    ' important!
    daSrc.AcceptChangesDuringFill = False

    dtSample = New DataTable
    rowsLoaded = daSrc.Fill(dtSample)

    ' csv lacks an ID column - add it
    Dim dc As New DataColumn("Id", GetType(Int32))
    dc.DefaultValue = 1
    dtSample.Columns.Add(dc)
    dc.SetOrdinal(0)

    ' MY csv also lacks a BATCH column
    dc = New DataColumn("Batch", GetType(Int32))
    dc.DefaultValue = 1
    dtSample.Columns.Add(dc)
    dc.SetOrdinal(1)

    ' set the batch number
    ' each 5k rows == a batch
    Dim batch As Int32 = 1
    Dim counter As Int32 = 1
    For Each dr As DataRow In dtSample.Rows
        dr("Batch") = batch
        counter += 1
        If counter > 5000 Then
            counter = 0
            batch += 1
        End If
    Next

    ' now save the data to MySQL
    mysqlCon.Open()
    ' inserting 250k rows takes a while,
    ' use a transaction
    Using t As MySqlTransaction = mysqlCon.BeginTransaction
        rowsUpdated = daDest.Update(dtSample)
        t.Commit()
    End Using

End Using

' show the IMPORT in a dgv
dgv1.DataSource = dtSample
dgv1.Columns("Id").Visible = False

' report
sw.Stop()
Console.WriteLine(sw.ElapsedMilliseconds)

原理很简单:由于数据绑定到数据库,因此尽快将数据转换为DataTable。这里的诀窍是有2个DB提供者参与:OleDB读取csv和MySql以进行保存。

  • 通常,当DataAdapter填充DataTable时,所有行都设置为UnchangedAcceptChangesDuringFill = False将状态设置为Added,以便MySql适配器可以插入这些行。
  • CommandBuilder构建要从SELECT命令使用的INSERT SQL。
  • 我不知道serialals-rollno查询正在做什么,但我会在导入过程循环中运行查询。如果您需要设置的某些值取决于数据库中的值,请将它们加载到另一个DT中并从那里查询它们。有一些DataTable扩展方法可以很容易地找到行。
  • 同样,我不知道EnterDataIntoDatabase的作用,但您应该努力处理并准备DataTable中的所有导入数据,然后立即更新所有数据。

您似乎比批量或排序一堆行更多。上面的代码可以导入250k行,分配批号,并在1.2分钟内向MySql插入250k新行(几乎每秒3500行)。

如果批处理/序列发生器与CSV中的每个X行数相似,则您可以一次只加载7000行,设置值,保存该批处理,然后加载下一个7k行。这将限制任何时候加载的行数并减少应用程序使用的内存。我不确定它是否适用。

参考: