【问题标题】:Import EXCEL data to SQL Server without data conversion将 EXCEL 数据导入 SQL Server 无需数据转换
【发布时间】:2019-09-25 13:45:49
【问题描述】:

我正在尝试将 excel 数据插入到 sql server 表中。每一列的导入格式应与用户在源 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 的日期列中,我们收到了一些浮点值(来自用户的格式问题),如下所示

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

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

【问题讨论】:

  • 原因数 100 亿为什么 Excel 作为数据源不好...我建议将 Excel 中该列的单元格类型更改为适当的日期或文本。
  • @JacobH 我们已经通知了用户,但有时我们会收到相同的信息。由于数据以批量方式发送,因此需要任何方式处理。或者任何其他替代方式?
  • 浮点数的整数部分是自 30/12/1899 (excel) 以来的天数 - SQL Server 使用 01/01/1900 作为天数,因此您可以使用 Select 进行转换CAST(exceldatecolumn - 2 作为 SmallDateTime)。见stackoverflow.com/questions/13850605/…
  • 那么它对你有用吗?我希望看到您方面的一些更新。
  • @tukan 它不起作用...它仍在将日期值转换为浮点数

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


【解决方案1】:

我认为您应该首先从 SQL 服务器表中获取数据类型来创建记录集,而不是让 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

【讨论】:

    【解决方案2】:

    您使用 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 个字符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-26
      • 2018-04-17
      • 2015-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多