无需数据转换即可将EXCEL数据导入SQL Server

时间:2019-09-25 13:45:49

标签: sql-server excel oledb linked-server openrowset

我正在尝试将excel数据插入sql服务器表中。每列均应使用用户在源Excel中编写的完全相同的格式导入。

我正在使用以下查询来获取数据

SELECT * FROM OPENROWSET( 'Microsoft.ACE.OLEDB.12.0', 'Excel 12.0 Xml;HDR=YES;IMEX=1;Database=H:\Loadloandata\Test\K3.xlsx', 
'SELECT * FROM [Sheet1$]')

但是现在在excel的日期列中,我们收到一些浮点值(来自用户的格式问题),如下所示

enter image description here

由于无效数据,OLE提供程序将所有其他日期转换为对应的SQL表列中的浮点值(与每个日期对应的浮点值)。如果将日期列自动强制转换为浮点数,我将无法知道excel文件中数据的原始格式,因此所有列都应作为varchar导入。

enter image description here

如何防止这种数据类型转换?基于谷歌搜索,我已经在连接字符串中使用IMEX = 1来检索混合数据列的数据。 但这是行不通的!

2 个答案:

答案 0 :(得分:0)

我认为您应该首先从SQL Server表中获取数据类型以创建记录集,而不是让Excel从工作表中确定数据类型。我相信Excel会根据第一行来决定数据类型,因此在您的情况下,它假定Funded数据是整数,然后将任何后续字符串转换为该数据类型。

这是我使用的完整功能

此代码由我从原始来源(在注释中提到)进行了修改。我进行了更改以更好地处理错误。

    Function ExportRangeToSQL(ByVal sourcerange As Range, _
    ByVal conString As String, ByVal table As String, _
    Optional ByVal beforeSQL = "", Optional ByVal afterSQL As String) As String

    'https://www.excel-sql-server.com/excel-sql-server-import-export-using-vba.htm

    ' Object type and CreateObject function are used instead of ADODB.Connection,
    ' ADODB.Command for late binding without reference to
    ' Microsoft ActiveX Data Objects 2.x Library
    ' ADO API Reference
    ' https://msdn.microsoft.com/en-us/library/ms678086(v=VS.85).aspx
    ' Dim con As ADODB.Connection

    On Error GoTo Finalise ' throw friendly user connection error

    Dim con As Object
    Set con = CreateObject("ADODB.Connection")

    con.ConnectionString = conString
    con.Open


    Dim cmd As Object
    Set cmd = CreateObject("ADODB.Command")

    ' BeginTrans, CommitTrans, and RollbackTrans Methods (ADO)
    ' http://msdn.microsoft.com/en-us/library/ms680895(v=vs.85).aspx

    Dim level As Long
    level = con.BeginTrans

    cmd.CommandType = 1             ' adCmdText
    If beforeSQL > "" Then
        cmd.CommandText = beforeSQL
        cmd.ActiveConnection = con
        cmd.Execute
    End If

    ' Dim rst As ADODB.Recordset
    Dim rst As Object
    Set rst = CreateObject("ADODB.Recordset")

    With rst
        Set .ActiveConnection = con
        .Source = "SELECT * FROM " & table
        .CursorLocation = 3         ' adUseClient
        .LockType = 4               ' adLockBatchOptimistic
        .CursorType = 0             ' adOpenForwardOnly
        .Open

        ' Column mappings

        Dim tableFields(100) As Integer
        Dim rangeFields(100) As Integer

        Dim exportFieldsCount As Integer
        exportFieldsCount = 0

        Dim col As Integer
        Dim index As Variant

        For col = 0 To .Fields.Count - 1
            index = 0
            index = Application.Match(.Fields(col).Name, sourcerange.Rows(1), 0)
            If Not IsError(index) Then
                If index > 0 Then
                    exportFieldsCount = exportFieldsCount + 1
                    tableFields(exportFieldsCount) = col
                    rangeFields(exportFieldsCount) = index
                End If
            End If
        Next

        If exportFieldsCount = 0 Then
            Err.Raise 513, , "Column mapping mismatch between source and destination tables"
        End If

        ' Fast read of Excel range values to an array
        ' for further fast work with the array

        Dim arr As Variant
        arr = sourcerange.Value

        ' The range data transfer to the Recordset

        Dim row As Long
        Dim rowCount As Long
        rowCount = UBound(arr, 1)

        Dim val As Variant

        For row = 2 To rowCount
            .AddNew
            For col = 1 To exportFieldsCount
                val = arr(row, rangeFields(col))
                If IsEmpty(val) Then
                Else
                    .Fields(tableFields(col)) = val
                End If
            Next
        Next

        .UpdateBatch
    End With

    rst.Close
    Set rst = Nothing

    If afterSQL > "" Then
        cmd.CommandText = afterSQL
        cmd.ActiveConnection = con
        cmd.Execute
    End If



Finalise:
If con.State <> 0 Then
    con.CommitTrans
    con.Close
End If

Set cmd = Nothing
Set con = Nothing

' Raise appropriate custom errors

Select Case Err.Number
    Case -2147217843
        Err.Raise 513, , "Issue connecting to SQL server database - please check login credentials"

    Case -2147467259
        If InStr(1, Err.Description, "Server does not exist") <> 0 Then
            Err.Raise 513, , "Could not connect to SQL server, please check you are connected to the local network (in the office or on VPN)"
        Else
             Err.Raise 513, , "Issue connecting to SQL server database" & vbNewLine & Err.Description
        End If
    Case -2147217900
        If InStr(1, Err.Description, "'PK_XL_Eng_Projects_QuoteRef'") <> 0 Then
             Err.Raise 513, , "Quote already uploaded for this QuoteRef and Upload Time, please wait a minute before trying again" & vbNewLine & vbNewLine & Err.Description
         Else
            Err.Raise Err.Number, , Err.Description
         End If
    Case 0
        ' do nothing no error
    Case Else
        ' re raise standard error
         Err.Raise Err.Number, , Err.Description

End Select


End Function

答案 1 :(得分:-1)

使用SSIS是否有原因?我认为最适合这份工作。

无论如何,回到您的问题。 IMEX=1是不够的。您需要检查注册表项

您需要在此注册表路径中设置TypeGuessRowsImportMixedTypes(这是针对32位办公室的!):

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office\12.0\Access Connectivity Engine\Engines\Excel

TypeGuessRows = 0 (the default is 8)

ImportMixedTypes = Text

TypeGuessRows做什么?

它尝试根据定义的行数来猜测数据类型。 默认值为8。这意味着它将检查8行以查看应使用哪种数据类型。如果要引擎扫描所有行,请在其中放置 0 。但是有一个问题,如果您的电子表格很大,您可能会为此设置付出沉重的性能损失。

ImportMixedTypes做什么?

这是您的IMEX设置进​​入游戏的地方。 IMEX设置012共有3种可能的值:

  • 0为导出模式
  • 1是导入模式
  • 2是链接模式(完全更新功能)

仅在设置IMEX=1时使用注册表值。 -默认设置为ImportMixedTypes=Text。对于其他任何值(0、2),都将检查注册表中的值(如果有效),但不会影响结果。 (如果无效,则会出现错误)

ImportMixedTypes有两个有效值:

ImportMixedTypes=Text
ImportMixedTypes=Majority Type

Majority Type很少使用。它所做的是计算每列的类型,然后将多数类型用于整个列。 Text类型会将行大小限制为255个字符,如果要使用更多字符,则必须使用Majority Type,并且大多数字符必须使用256个以上字符。

相关问题