【问题标题】:get column names Jet OLE DB in vb.net在 vb.net 中获取列名 Jet OLE DB
【发布时间】:2016-10-09 20:27:23
【问题描述】:

我编写了一个函数,它读取 csv 文件并相应地对其进行参数化,因此我有一个函数 gettypessql,它首先查询 sql 表以获取数据类型,从而调整稍后插入 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,这个不能正确转换,尤其是 doubles

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

【问题讨论】:

  • 连接字符串的HDR=Yes 部分是第一行是否有列名(而不是您是否要使用这些名称)。如果在没有标题时使用 YES,则会跳过一行。有几种方法可以覆盖和指定要使用的列名,还有一种更简单的方法可以在不解析和转换的情况下指定类型。我只是不确定问题是什么。
  • ..另外,该代码是要作用于特定的文件-表对还是您正在尝试编写通用的 csv-to-table 处理器?
  • 好吧,实际上它应该处理所有 csv 文件,但我发现文件包含一些错误。但无论如何,当我运行程序时,它并没有真正将所有列正确转换为特定类型,例如它将双精度转换为数据,原因不明
  • 如果是通用导入器,从3个方法中怎么知道哪些列需要处理?在不需要解析/编码/等的列上调用它们会产生不良影响吗?
  • function gettypessql 只是查询已经使用所有数据类型创建的 sql 表,它只是获取它们并保存在 mListtypes 中,然后将其分配给使用 oledb 读取的所有列,然后处理所有行这是字符串。 csv导入器自动知道需要处理什么

标签: vb.net oledb sqldatatypes jet


【解决方案1】:

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

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

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


以下使用一个类将 csv 列映射到 db 表列,因此代码不依赖于 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 可以将它们作为解析类型的一部分应用。


潜在的灾难正在等待发生,因为这是一个通用的进口商/洗涤器。

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 中的列名和目标表上的 SELECT * 查询中的 DataTypes 来构建新表。因此,它假定 CSV 列的顺序与 SELECT * 将返回它们的顺序相同,并且所有 CSV 将始终使用与表相同的名称。

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

更强大的解决方案是编写一个小型实用程序应用程序,用户将数据库列名称映射到 CSV 索引。将结果保存到 List(Of CSVMapItem) 并对其进行序列化。可能有一个完整的这些保存到磁盘的集合。然后,无需根据航位推算创建地图,只需将用户所需的反序列化为上述代码中的csvMap

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    相关资源
    最近更新 更多