2016-06-09 5 views
2

나는 csv 파일을 읽고 그에 따라 매개 변수를 작성하는 함수를 작성 했으므로 먼저 데이터 유형을 얻고 열을 조정하기 위해 SQL 테이블을 쿼리하는 gettypessql 함수가 있습니다. 나중에 sql에 삽입됩니다. 그래서 제 문제는 내가 Jet OLE DB에서 HDR을 Yes로 설정했을 때 F1, F2, F3과 같은 열 이름 만 얻는 것입니다. 이 문제를 피하기 위해 HDR = No로 설정하고 루프 용으로 작성했지만 이제는 빈 문자열 만 얻습니다. 실제로 문제는 무엇입니까? 당신이 그것을 더 복잡가 필요 이상 만들었 생각열 이름을 얻으십시오 vb.net의 Jet OLE DB

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에서 데이터 유형을 수집하여 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를 사용하지만 코드를 그냥 문자열 배열로 CSV 행을 읽어 때문에이 경우 TextFieldParser이 사용될 수있다.

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 분. 이러한 문자열 변환을 모든 단일 문자열 열에 적용해야하는 앱은 훨씬 오래 걸립니다. CSVHelperCsvReader을 사용하면 Type에 구문 분석의 일부로 적용 할 수 있다고 확신합니다.


이것은 다목적 수입/스크러버를위한 ​​것이므로 잠재적 인 재앙이 기다리고 있습니다.

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 

질문과 자기 답변을 대상 테이블에 SELECT * 쿼리에서 CSV 및 데이터 유형에서 열 이름을 사용하여 새 테이블을 만들 둘. 따라서 CSV 열의 순서는 동일하므로 SELECT *이 반환하고 모든 CSV는 항상 테이블과 동일한 이름을 사용한다고 가정합니다.

위의 대답은 이름을 기반으로 찾고 일치한다는 점에서 조금 낫습니다.

더 강력한 솔루션은 사용자가 DB 열 이름을 CSV 색인에 매핑하는 작은 유틸리티 앱을 작성하는 것입니다. 결과를 List(Of CSVMapItem)에 저장하고 일련 번호를 지정하십시오. 디스크에 저장된 전체 모음이있을 수 있습니다. 그런 다음 데드 레커 닝을 기반으로지도를 만드는 대신 위의 코드에서 csvMap으로 사용자에게 원하는 것을 deserialize합니다.