【问题标题】:Bulk insert into sql from an array in VBA从VBA中的数组批量插入sql
【发布时间】:2014-01-17 05:18:53
【问题描述】:

我正在尝试在 excel 中构建一个按钮,该按钮会将所选区域上传到 sql server 中的表中。第一行将被自动视为列标题。

如何处理这个问题?我想要的是简单和超快速的上传。

这是我的想法---

我会获取选定的区域,然后将其保存为 .txt 文件,然后在其上运行批量插入语句。有没有一种方法可以直接批量插入所选区域(可能先将其放入变体数组中),而无需先将其保存为 .txt 文件?

另外,如果有更有效的方法,那就建议吧。

【问题讨论】:

  • sql server 在问题中提到但标记为mysql ?
  • 桌子可能有多大?
  • @whytheq 本身没有一般限制。从 10-10^7 行。
  • 10^7 ....来自 Excel - 怎么样? (Excel 工作表中有 1 个轧机行)

标签: sql sql-server vba excel


【解决方案1】:

我将从与服务器的简单 ADO 连接开始,然后执行一堆 INSERT INTO 语句,看看它的性能如何。如果这不起作用,那么您可以查看使用相同的 INSERT INTO 语句创建一个文本文件。那可能更快,我真的不知道。这是一些将范围转换为您需要的 SQL 语句的代码

Function RangeToInsert(rRng As Range) As String

    Dim vaData As Variant
    Dim i As Long, j As Long
    Dim aReturn() As String
    Dim aCols() As String
    Dim aVals() As Variant

    Const sINSERT As String = "INSERT INTO MyTable "
    Const sVAL As String = " VALUES "

    'Read in data
    vaData = rRng.Value

    'Create arrays
    ReDim aReturn(1 To UBound(vaData))
    ReDim aCols(1 To UBound(vaData, 2))
    ReDim aVals(1 To UBound(vaData, 2))

    'Fill column name array from first row
    For j = LBound(vaData, 2) To UBound(vaData, 2)
        aCols(j) = vaData(1, j)
    Next j

    'Go through the rest of the rows
    For i = LBound(vaData, 1) + 1 To UBound(vaData, 1)

        'Fill a temporary array
        For j = LBound(vaData, 2) To UBound(vaData, 2)
            aVals(j) = vaData(i, j)
        Next j

        'Build the string into the main array
        aReturn(i) = sINSERT & "(" & Join(aCols, ",") & ")" & sVAL & "(" & Join(aVals, ",") & ");"
    Next i

    RangeToInsert = Join(aReturn, vbNewLine)

End Function

以下是我拼凑的一些假数据的输出结果:

?rangetoinsert(selection)

INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (97,100,53,27,14,53,94,43);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (21,96,69,60,70,8,35,54);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (8,12,78,38,82,67,41,53);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (15,32,89,100,61,78,16,37);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (46,37,75,66,66,93,19,45);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (82,30,56,10,99,2,1,29);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (98,39,98,96,95,60,16,73);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (65,79,69,70,74,86,15,59);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (30,37,12,57,86,94,6,53);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (1,20,91,65,20,26,96,57);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (81,7,68,65,56,27,81,80);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (94,42,43,33,46,82,44,24);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (88,48,34,83,58,64,36,90);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (51,28,43,70,12,29,96,27);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (62,54,76,86,92,41,40,84);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (11,21,32,30,65,6,22,75);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (44,72,38,73,44,93,4,16);
INSERT INTO MyTable (F1,F2,F3,F4,F5,F6,F7,F8) VALUES (39,90,96,16,9,6,17,50);

您可以将其放入 ADO 连接上的 Execute 调用中,或将其写入文本文件。我无法想象还有什么比使用 SQL 语句更快。例如,您可以在循环通过可更新记录集时更新 .Fields.Value 属性,但没有比这更快的方法了。

如果您正在处理一百万行,那么 VBA 中的任何内容都不会很快。所以请记住这一点。

【讨论】:

  • "如果你要处理一百万行,那么 VBA 中的任何东西都不会很快" 虽然这通常是正确的(我自己说过),有一种解决方法:将数据转储到一个分隔的文本文件,然后使用 SQL Server BCP 实用程序将其导入。 Kludgy,但它是从 VBA 加载 100 万行的唯一真正快速的方法。
  • 仅供参考:使用 INSERT 语句方法的文本文件不太可能比直接 VBA 更快。
  • @RBarryYoung 批量插入语句确实是最快的。我对此毫不怀疑。问题在于将 excel 工作表转换为管道分隔的文本文件。我不知道这会有多快,因为我必须在每个单元格之后附加一个管道,然后将其打印到 txt 文件中。所以这意味着顺序(行数)*(列数)的强力双循环。这就是为什么我正在寻找一种从数组而不是 txt 文件批量插入的方法。
  • @DickKusleika 在这种情况下您将如何处理文本值,其中值需要用引号括起来?
  • @DickKusleika 检查,我也想通了,但我希望有一种更优雅的方式来做到这一点:) 谢谢
【解决方案2】:

首先你需要写一个函数(我的叫做插入表单)。

Function insertForm(givenRange As Range) As String
--Convert each row to a string format of values
--Each value needs to have all instances of ' replaced with ''
--Each value needs to be wrapped in '
--Use Join with comma(,) to join all the values and put brackets around the whole thing
--This is the insert form for one row.
--You can get the other rows by writing the result of the function recursively
--If givenRange.Rows.Count > 1 Then
    --insertForm = insertForm & ", " & insertForm(givenRange.Offset(1, 0).Resize(givenRange.Rows.Count - 1, givenRange.Columns.Count))
--End If
End Function

所以如果我选择 4 列 2 行包含

1 2 3 4
5 6 7 8

此函数创建 ('1','2','3','4'),('5','6','7','8') 然后我可以在 INSERT SQL 语句中使用它,如下所示:

INSERT INTO TableName VALUES InsertForm(Range)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多