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

时间:2016-06-09 09:15:21

标签: vb.net oledb sqldatatypes jet

我已经编写了一个函数来读取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,这个没有正确转换,特别是双打

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

1 个答案:

答案 0 :(得分:2)

我认为你使它变得比它需要的更复杂。例如,通过创建空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行更新一次DB。它也是一次通过,而不是一次通过OleDB读取N行。 CsvParser将一次读取一行,因此一次不会有超过50,001行的数据。

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

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

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

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

上面的答案略微好一点,因为它根据名称找到并匹配。

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