有没有人知道使用外部实用程序(即bcp)从SQL表获取数据的最快方法和SQL表上的表? 请记住,我的数据集通常是6500-15000行,大约150-250列;我最终在自动VBA批处理脚本中传输了大约20-150个。
我尝试了几种从Excel表(VBA)到SQL 2008获取大量数据的方法。我列出了以下内容:
方法1.将表传递到VBA阵列并发送到存储过程(ADO) - 发送到SQL是SLOW
方法2.创建断开连接的RecordSet加载它,然后同步。 - 发送到非常慢的SQL
方法3.将表放入VBA数组,循环遍历数组并连接(使用分隔符),然后发送到存储过程。 - 发送到SQL SLOW,但比方法1或2更快。
方法4.将表放入VBA数组,循环遍历数组并连接(使用分隔符),然后将每行放置ADO recordset .addnew命令。 - 发送到SQL非常快(比方法1-3快约20倍),但现在我需要使用单独的过程拆分数据,这将增加大量的等待时间。
方法5.将表放入VBA数组,序列化为XML,作为VARCHAR发送到存储过程并在存储过程中指定XML。 - 发送到SQL INCREDIBLY SLOW(比方法1或2慢大约100倍)
我缺少什么?
答案 0 :(得分:2)
没有一种最快的方式,因为它取决于许多因素。确保SQL中的索引已配置和优化。由于每个插入都需要更新索引,因此大量索引会终止插入/更新性能。确保只与数据库建立一个连接,并且在操作期间不要打开/关闭它。在服务器处于最小负载时运行更新。您还没有尝试过的唯一其他方法是使用ADO Command对象,并发出直接INSERT语句。使用记录集对象的“AddNew”方法时,请确保在插入的末尾只发出一个“UpdateBatch”命令。除此之外,VBA只能以接受输入的SQL服务器的速度运行。
编辑: 好像你已经尝试了一切。 SQL Server中还有一种所谓的“批量记录”恢复模式,可以减少写入事务日志的过多开销。可能是值得研究的事情。它可能很麻烦,因为它需要稍微调整数据库恢复模型,但它可能对您有用。
答案 1 :(得分:2)
以下代码将在几秒钟(2-3秒)内传输数千个数据。
Dim sheet As Worksheet
Set sheet = ThisWorkbook.Sheets("DataSheet")
Dim Con As Object
Dim cmd As Object
Dim ServerName As String
Dim level As Long
Dim arr As Variant
Dim row As Long
Dim rowCount As Long
Set Con = CreateObject("ADODB.Connection")
Set cmd = CreateObject("ADODB.Command")
ServerName = "192.164.1.11"
'Creating a connection
Con.ConnectionString = "Provider=SQLOLEDB;" & _
"Data Source=" & ServerName & ";" & _
"Initial Catalog=Adventure;" & _
"UID=sa; PWD=123;"
'Setting provider Name
Con.Provider = "Microsoft.JET.OLEDB.12.0"
'Opening connection
Con.Open
cmd.CommandType = 1 ' adCmdText
Dim Rst As Object
Set Rst = CreateObject("ADODB.Recordset")
Table = "EmployeeDetails" 'This should be same as the database table name.
With Rst
Set .ActiveConnection = Con
.Source = "SELECT * FROM " & Table
.CursorLocation = 3 ' adUseClient
.LockType = 4 ' adLockBatchOptimistic
.CursorType = 0 ' adOpenForwardOnly
.Open
Dim tableFields(200) As Integer
Dim rangeFields(200) As Integer
Dim exportFieldsCount As Integer
exportFieldsCount = 0
Dim col As Integer
Dim index As Integer
index = 1
For col = 1 To .Fields.Count
exportFieldsCount = exportFieldsCount + 1
tableFields(exportFieldsCount) = col
rangeFields(exportFieldsCount) = index
index = index + 1
Next
If exportFieldsCount = 0 Then
ExportRangeToSQL = 1
GoTo ConnectionEnd
End If
endRow = ThisWorkbook.Sheets("DataSheet").Range("A65536").End(xlUp).row 'LastRow with the data.
arr = ThisWorkbook.Sheets("DataSheet").Range("A1:CE" & endRow).Value 'This range selection column count should be same as database table column count.
rowCount = UBound(arr, 1)
Dim val As Variant
For row = 1 To rowCount
.AddNew
For col = 1 To exportFieldsCount
val = arr(row, rangeFields(col))
.Fields(tableFields(col - 1)) = val
Next
Next
.UpdateBatch
End With
flag = True
'Closing RecordSet.
If Rst.State = 1 Then
Rst.Close
End If
'Closing Connection Object.
If Con.State = 1 Then
Con.Close
End If
'Setting empty for the RecordSet & Connection Objects
Set Rst = Nothing
Set Con = Nothing
End Sub
答案 2 :(得分:0)
到目前为止,最快的方法是通过T-SQL的BULK INSERT
。
有一些警告。
BULK INSERT
命令并指定文件名时,请记住文件名将在运行SQL Server的计算机上解析)。FIELDTERMINATOR
和ROWTERMINATOR
值以匹配您的CSV。我最初设置这个设置需要一些试验和错误,但与我尝试过的其他技术相比,性能提升是惊人的。
答案 3 :(得分:0)
工作得很好,另一方面为了提高速度,我们仍然可以修改查询:
相反:Source = "SELECT * FROM " & Table
我们可以使用:Source = "SELECT TOP 1 * FROM " & Table
这是我们只需要列名。因此,无需对整个表进行查询,只要导入新数据,就会扩展该过程。
答案 4 :(得分:0)
据我记忆,您可以为Excel文件创建链接服务器(只要服务器可以找到路径;最好将文件放在服务器的本地磁盘上)然后使用SQL从中检索数据。
答案 5 :(得分:0)
刚刚尝试了几种方法,我又回到了一个相对简单但速度很快的方法。它很快,因为它使SQL服务器完成所有工作,包括有效的执行计划。
我只是构建一个包含INSERT语句脚本的长字符串。
Public Sub Upload()
Const Tbl As String = "YourTbl"
Dim InsertQuery As String, xlRow As Long, xlCol As Integer
Dim DBconnection As New ADODB.Connection
DBconnection.Open "Provider=SQLOLEDB.1;Password=MyPassword" & _
";Persist Security Info=false;User ID=MyUserID" & _
";Initial Catalog=MyDB;Data Source=MyServer"
InsertQuery = ""
xlRow = 2
While Cells(xlRow, 1) <> ""
InsertQuery = InsertQuery & "INSERT INTO " & Tbl & " VALUES('"
For xlCol = 1 To 6 'Must match the table structure
InsertQuery = InsertQuery & Replace(Cells(xlRow, xlCol), "'", "''") & "', '" 'Includes mitigation for apostrophes in the data
Next xlCol
InsertQuery = InsertQuery & Format(Now(), "M/D/YYYY") & "')" & vbCrLf 'The last column is a date stamp, either way, don't forget to close that parenthesis
xlRow = xlRow + 1
Wend
DBconnection.Execute InsertQuery 'I'll leave any error trapping to you
DBconnection.Close 'But do be tidy :-)
Set DBconnection = Nothing
End Sub