2016-06-09 70 views
2

我寫了一個函數讀取CSV文件,並相應parametrizes過的,所以我有一個功能gettypessql起初查詢SQL表得到的數據類型,因此調整列後來插入到sql中。所以我的問題是,當我在Jet OLE DB中設置HDR = Yes時,我只得到像F1,F2,F3這樣的列名。爲了避免這個問題,我設置了HDR = No,並且寫了一些for循環,但現在我只得到空字符串,實際上是什麼問題?這裏是我的代碼:獲得列名的Jet OLE DB在vb.net

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 
+0

連接字符串的「HDR = Yes」部分是第一行是否有列名(而不是您是否要使用這些名稱)。如果您在沒有標題時使用YES,則會跳過一行。有幾種方法可以覆蓋和指定要使用的列名,也可以使用一種更簡單的方式指定類型,而無需解析和轉換。我只是不確定問題是什麼。 – Plutonix

+0

..此外,是代碼意味着作用於一個特定的文件表對,或者你是否試圖編寫一個通用的csv到表處理器? – Plutonix

+0

好吧,實際上它應該處理所有的csv文件,但我發現文件包含一些錯誤。但無論如何,當我運行該程序時,它並沒有真正將所有列正確轉換爲特定類型,例如,它將double轉換爲數據,原因不明 – Sparkm4n

回答

2

我想你已經變得更加複雜得多,它需要的。例如,您可以通過創建一個空DataTable並從中收穫的數據類型得到的dbschema。爲什麼不使用第一個表而不是從類型創建新表?對於每批輸入的行,該表也不需要重新構建。

因爲OleDb通常會嘗試從數據推斷出類型,似乎沒有必要,甚至可以在某些情況下得到的方式。此外,您正在重做OleDB所做的一切,並將數據複製到不同的DT。鑑於此,我會跳過OleDB的開銷並使用原始數據。

這將創建使用CSV列名和數據庫類型的目標表。如果CSV與SELECT *查詢中提供的列順序不同,那麼它將失敗。


下面使用一個映射類CSV列,以便將代碼不依賴於CSV中以相同的順序被(因爲它們可以被外部產生)db表的列。我的樣本數據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 *查詢建新表。因此,它假設CSV列與SELECT *將返回它們的順序相同,並且所有CSV將始終使用與表格相同的名稱。

上面的答案稍微好一點,因爲它根據名稱查找和匹配。

更強大的解決方案是編寫一個小實用程序應用程序,其中用戶將DB列名映射到CSV索引。將結果保存到List(Of CSVMapItem)並將其序列化。可能會有這些保存到磁盤的整個集合。然後,不是根據航位推算創建地圖,而是將上述代碼中的用戶期望的序列化爲csvMap