在 vb.net 中获取列名 Jet OLE DB

get column names Jet OLE DB in vb.net

我写了一个函数来读取 csv 文件并相应地对其进行参数化,因此我有一个函数 gettypessql 首先查询 sql table 以获取数据类型和因此要调整稍后插入 sql 中的列。所以我的问题是当我在 Jet OLE DB 中设置 HDR=Yes 时,我只得到像 F1、F2、F3 这样的列名。为了避免这个问题,我设置了 HDR=No 并编写了一些 for 循环,但现在我只得到空字符串,实际上是什么问题?这是我的代码:

 Private Function GetCSVFile(ByVal file As String, ByVal min As Integer, ByVal max As Integer) As DataTable
        Dim ConStr As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & TextBox1.Text & ";Extended Properties=""TEXT;HDR=NO;IMEX=1;FMT=Delimited;CharacterSet=65001"""
        Dim conn As New OleDb.OleDbConnection(ConStr)
        Dim dt As New DataTable
        Dim da As OleDb.OleDbDataAdapter = Nothing
        getData = Nothing

        Try
            Dim CMD As String = "Select * from " & _table & ".csv"
            da = New OleDb.OleDbDataAdapter(CMD, conn)
            da.Fill(min, max, dt)
            getData = New DataTable(_table)
            Dim firstRow As DataRow = dt.Rows(0)  

            For i As Integer = 0 To dt.Columns.Count - 1
                Dim columnName As String = firstRow(i).ToString()
                Dim newColumn As New DataColumn(columnName, mListOfTypes(i))
                getData.Columns.Add(newColumn)
            Next

            For i As Integer = 1 To dt.Rows.Count - 1
                Dim row As DataRow = dt.Rows(i)
                Dim newRow As DataRow = getData.NewRow()

                For j As Integer = 0 To getData.Columns.Count - 1
                    If row(j).GetType Is GetType(String) Then
                        Dim colValue As String = row(j).ToString()
                        colValue = ChangeEncoding(colValue)
                        colValue = ParseString(colValue)
                        colValue = ReplaceChars(colValue)
                        newRow(j) = colValue
                    Else
                        newRow(j) = row(j)
                    End If
                Next

                getData.Rows.Add(newRow)
                Application.DoEvents()
            Next
        Catch ex As OleDbException
            MessageBox.Show(ex.Message)
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        Finally
            dt.Dispose()
            da.Dispose()
        End Try

        Return getData
    End Function

并获取类型 sql,这个转换不正确,尤其是双打

Private Sub GetTypesSQL()
        If (mListOfTypes Is Nothing) Then
            mListOfTypes = New List(Of Type)()
        End If

        mListOfTypes.Clear()

        Dim dtTabelShema As DataTable = db.GetDataTable("SELECT TOP 0 * FROM " & _table)

        Using dtTabelShema
            For Each col As DataColumn In dtTabelShema.Columns
                mListOfTypes.Add(col.DataType)
            Next
        End Using
    End Sub

我认为你把它弄得比需要的更复杂了。例如,您通过创建一个空 DataTable 并从中获取数据类型来获取 dbSchema。为什么不直接使用第一个 table 而不是从类型中创建一个新的 table? table 也不需要为每批导入的行一遍又一遍地重建。

通常,由于 OleDb 会尝试从数据中推断类型,这似乎是不必要的,在某些情况下甚至会妨碍。此外,您正在重做 OleDB 所做的一切并将数据复制到不同的 DT。鉴于此,我将跳过 OleDB 强加的开销并使用原始数据。

这会使用数据库中的 CSV 列名称和类型创建目标 table。如果 CSV 与 SELECT * 查询中提供的列顺序不同,它将失败。


以下使用 class 将 csv 列映射到 db table 列,因此代码不依赖于顺序相同的 CSV(因为它们可能是在外部生成的)。我的示例数据 CSV 是 而不是 ,顺序相同:

Public Class CSVMapItem

    Public Property CSVIndex As Int32
    Public Property ColName As String = ""
   'optional
    Public Property DataType As Type

    Public Sub New(ndx As Int32, csvName As String,
                   dtCols As DataColumnCollection)

        CSVIndex = ndx

        For Each dc As DataColumn In dtCols
            If String.Compare(dc.ColumnName, csvName, True) = 0 Then
                ColName = dc.ColumnName
                DataType = dc.DataType
                Exit For
            End If
        Next

        If String.IsNullOrEmpty(ColName) Then
            Throw New ArgumentException("Cannot find column: " & csvName)
        End If
    End Sub
End Class

解析 csv 的代码使用 CSVHelper,但在本例中可以使用 TextFieldParser,因为代码只是将 CSV 行读入字符串数组。

Dim SQL = String.Format("SELECT * FROM {0} WHERE ID<0", DBTblName)
Dim rowCount As Int32 = 0
Dim totalRows As Int32 = 0
Dim sw As New Stopwatch
sw.Start()

Using dbcon As New MySqlConnection(MySQLConnStr)
    Using cmd As New MySqlCommand(SQL, dbcon)

        dtSample = New DataTable
        dbcon.Open()

        ' load empty DT, create the insert command
        daSample = New MySqlDataAdapter(cmd)
        Dim cb = New MySqlCommandBuilder(daSample)
        daSample.InsertCommand = cb.GetInsertCommand
        dtSample.Load(cmd.ExecuteReader())

        ' dtSample is not only empty, but has the columns
        ' we need

        Dim csvMap As New List(Of CSVMapItem)

        Using sr As New StreamReader(csvfile, False),
                        parser = New CsvParser(sr)

            ' col names from CSV
            Dim csvNames = parser.Read()
            ' create a map of CSV index to DT Columnname  SEE NOTE
            For n As Int32 = 0 To csvNames.Length - 1
                csvMap.Add(New CSVMapItem(n, csvNames(n), dtSample.Columns))
            Next

            ' line data read as string
            Dim data As String()
            data = parser.Read()
            Dim dr As DataRow

            Do Until data Is Nothing OrElse data.Length = 0

                dr = dtSample.NewRow()

                For Each item In csvMap
                    ' optional/as needed type conversion
                    If item.DataType = GetType(Boolean) Then
                        ' "1" wont convert to bool, but (int)1 will
                        dr(item.ColName) = Convert.ToInt32(data(item.CSVIndex).Trim)
                    Else
                        dr(item.ColName) = data(item.CSVIndex).Trim
                    End If
                Next
                dtSample.Rows.Add(dr)
                rowCount += 1

                data = parser.Read()

                If rowCount = 50000 OrElse (data Is Nothing OrElse data.Length = 0) Then
                    totalRows += daSample.Update(dtSample)
                    ' empty the table if there will be more than 100k rows
                    dtSample.Rows.Clear()
                    rowCount = 0
                End If
            Loop
        End Using

    End Using
End Using
sw.Stop()
Console.WriteLine("Parsed and imported {0} rows in {1}", totalRows,
                    sw.Elapsed.TotalMinutes)

如果有很多行,处理循环每 50K 行更新一次数据库。它还一次性完成,而不是一次通过 OleDB 读取 N 行。 CsvParser 将一次读取一行,因此手头的数据一次不应超过 50,001 行。

If item.DataType = GetType(Boolean) Then 所示,可能需要处理类型转换的特殊情况。读入为“1”的布尔列不能直接传递给布尔列,所以它被转换为可以的整数。可能还有其他转换,例如时髦的日期。

处理 250,001 行的时间:3.7 分钟。需要将这些字符串转换应用于每个字符串列的应用程序将花费更长的时间。我很确定在 CSVHelper 中使用 CsvReader 你可以将它们作为解析类型的一部分应用。


有一个潜在的灾难等待发生,因为这是一个通用的 importer/scrubber。

For i As Integer = 0 To dt.Columns.Count - 1
    Dim columnName As String = firstRow(i).ToString()
    Dim newColumn As New DataColumn(columnName, mListOfTypes(i))
    getData.Columns.Add(newColumn)
Next

问题和自我回答都使用来自 CSV 的列名和来自目标 table 的 SELECT * 查询的数据类型构建新的 table。因此,它假定 CSV 列的顺序与 SELECT * 将 return 它们的顺序相同,并且所有 CSV 将始终使用与 table 相同的名称。

上面的答案稍微好一些,因为它根据名称查找和匹配。

一个更强大的解决方案是编写一个小实用程序,用户可以在其中将数据库列名称映射到 CSV 索引。将结果保存到 List(Of CSVMapItem) 并序列化。可以将这些的整个集合保存到磁盘中。然后,不是基于航位推算创建地图,而是将用户所需的反序列化为上面代码中的 csvMap