我正在使用ADO将数据保存到MS Access数据库。将数据保存到文件需要相当长的时间(大约7秒 - 这对于我们的目的来说太长了)。我查看了正在运行的SQL查询的数量,它大约是4200;虽然没有一大堆数据。
数据库连接似乎是瓶颈。你知道如何减少这个时间吗?要么通过某种方式将多个语句合并为一个以减少开销,或者某些ADO / MS-Access技巧?
例如,您可以一次向表中插入多行,这会明显加快吗?
额外信息:
我们有这么多查询的一个原因是我们插入一行,然后有另一个查询来检索其自动增加的ID;然后使用此ID插入更多行,将它们链接到第一个
回应几条评论和回复:我将整个时间打开连接,并将其作为单个事务执行,使用BeginTransaction()和CommitTransaciton()
答案 0 :(得分:5)
有些人发布@@IDENTITY
会很快,所以这里有一个证明(使用VBA)我的INSERT INTO
两个表一次通过VIEW
技巧的速度大约快三倍而不是每次执行两次INSERTS并获取@@IDENTITY
值......这并不奇怪,因为后者涉及三个Execute
语句而前者只涉及一个:)
在4200次迭代的机器上,VIEW
技巧花了45秒,而@@IDENTITY
方法耗时127秒:
Sub InitInerts()
On Error Resume Next
Kill Environ$("temp") & "\DropMe.mdb"
On Error GoTo 0
Dim cat
Set cat = CreateObject("ADOX.Catalog")
With cat
.Create _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
With .ActiveConnection
Dim Sql As String
Sql = _
"CREATE TABLE TableA" & vbCr & "(" & vbCr & " ID IDENTITY NOT" & _
" NULL UNIQUE, " & vbCr & " a_col INTEGER NOT NULL" & vbCr & ")"
.Execute Sql
Sql = _
"CREATE TABLE TableB" & vbCr & "(" & vbCr & " ID INTEGER NOT" & _
" NULL UNIQUE" & vbCr & " REFERENCES TableA (ID)," & _
" " & vbCr & " b_col INTEGER NOT NULL" & vbCr & ")"
.Execute Sql
Sql = _
"CREATE VIEW TestAB" & vbCr & "(" & vbCr & " a_ID, a_col, " & vbCr & " " & _
" b_ID, b_col" & vbCr & ")" & vbCr & "AS " & vbCr & "SELECT A1.ID, A1.a_col," & _
" " & vbCr & " B1.ID, B1.b_col" & vbCr & " FROM TableA AS" & _
" A1" & vbCr & " INNER JOIN TableB AS B1" & vbCr & " " & _
" ON A1.ID = B1.ID"
.Execute Sql
End With
Set .ActiveConnection = Nothing
End With
End Sub
Sub TestInerts_VIEW()
Dim con
Set con = CreateObject("ADODB.Connection")
With con
.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
Dim timer As CPerformanceTimer
Set timer = New CPerformanceTimer
timer.StartTimer
Dim counter As Long
For counter = 1 To 4200
.Execute "INSERT INTO TestAB (a_col, b_col) VALUES (" & _
CStr(counter) & ", " & _
CStr(counter) & ");"
Next
Debug.Print "VIEW = " & timer.GetTimeSeconds
End With
End Sub
Sub TestInerts_IDENTITY()
Dim con
Set con = CreateObject("ADODB.Connection")
With con
.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
Dim timer As CPerformanceTimer
Set timer = New CPerformanceTimer
timer.StartTimer
Dim counter As Long
For counter = 1 To 4200
.Execute "INSERT INTO TableA (a_col) VALUES (" & _
CStr(counter) & ");"
Dim identity As Long
identity = .Execute("SELECT @@IDENTITY;")(0)
.Execute "INSERT INTO TableB (ID, b_col) VALUES (" & _
CStr(identity) & ", " & _
CStr(counter) & ");"
Next
Debug.Print "@@IDENTITY = " & timer.GetTimeSeconds
End With
End Sub
这表明现在的瓶颈是与执行多个语句相关的开销。如果我们能在一个声明中做到这一点怎么办?好吧,猜猜是什么,用我惯用的例子,我们可以。首先,创建一个唯一整数的Sequence表,这是一个标准的SQL技巧(每个数据库应该有一个,IMO):
Sub InitSequence()
Dim con
Set con = CreateObject("ADODB.Connection")
With con
.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
Dim sql As String
sql = _
"CREATE TABLE [Sequence]" & vbCr & "(" & vbCr & " seq INTEGER NOT NULL" & _
" UNIQUE" & vbCr & ");"
.Execute sql
sql = _
"INSERT INTO [Sequence] (seq) VALUES (-1);"
.Execute sql
sql = _
"INSERT INTO [Sequence] (seq) SELECT Units.nbr + Tens.nbr" & _
" + Hundreds.nbr + Thousands.nbr AS seq FROM ( SELECT" & _
" nbr FROM ( SELECT 0 AS nbr FROM [Sequence] UNION" & _
" ALL SELECT 1 FROM [Sequence] UNION ALL SELECT 2 FROM" & _
" [Sequence] UNION ALL SELECT 3 FROM [Sequence] UNION" & _
" ALL SELECT 4 FROM [Sequence] UNION ALL SELECT 5 FROM" & _
" [Sequence] UNION ALL SELECT 6 FROM [Sequence] UNION" & _
" ALL SELECT 7 FROM [Sequence] UNION ALL SELECT 8 FROM" & _
" [Sequence] UNION ALL SELECT 9 FROM [Sequence] ) AS" & _
" Digits ) AS Units, ( SELECT nbr * 10 AS nbr FROM" & _
" ( SELECT 0 AS nbr FROM [Sequence] UNION ALL SELECT" & _
" 1 FROM [Sequence] UNION ALL SELECT 2 FROM [Sequence]" & _
" UNION ALL SELECT 3 FROM [Sequence] UNION ALL SELECT" & _
" 4 FROM [Sequence] UNION ALL SELECT 5 FROM [Sequence]" & _
" UNION ALL SELECT 6 FROM [Sequence] UNION ALL SELECT" & _
" 7 FROM [Sequence] UNION ALL SELECT 8 FROM [Sequence]" & _
" UNION ALL SELECT 9 FROM [Sequence] ) AS Digits )" & _
" AS Tens, ( SELECT nbr * 100 AS nbr FROM ( SELECT" & _
" 0 AS nbr FROM [Sequence] UNION ALL SELECT 1 FROM" & _
" [Sequence] UNION ALL SELECT 2 FROM [Sequence] UNION"
sql = sql & _
" ALL SELECT 3 FROM [Sequence] UNION ALL SELECT 4 FROM" & _
" [Sequence] UNION ALL SELECT 5 FROM [Sequence] UNION" & _
" ALL SELECT 6 FROM [Sequence] UNION ALL SELECT 7 FROM" & _
" [Sequence] UNION ALL SELECT 8 FROM [Sequence] UNION" & _
" ALL SELECT 9 FROM [Sequence] ) AS Digits ) AS Hundreds," & _
" ( SELECT nbr * 1000 AS nbr FROM ( SELECT 0 AS nbr" & _
" FROM [Sequence] UNION ALL SELECT 1 FROM [Sequence]" & _
" UNION ALL SELECT 2 FROM [Sequence] UNION ALL SELECT" & _
" 3 FROM [Sequence] UNION ALL SELECT 4 FROM [Sequence]" & _
" UNION ALL SELECT 5 FROM [Sequence] UNION ALL SELECT" & _
" 6 FROM [Sequence] UNION ALL SELECT 7 FROM [Sequence]" & _
" UNION ALL SELECT 8 FROM [Sequence] UNION ALL SELECT" & _
" 9 FROM [Sequence] ) AS Digits ) AS Thousands;"
.Execute sql
End With
End Sub
然后使用Sequence表枚举1到42000之间的值,并在单个INSERT INTO..SELECT语句中构造行:
Sub TestInerts_Sequence()
Dim con
Set con = CreateObject("ADODB.Connection")
With con
.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
Dim timer As CPerformanceTimer
Set timer = New CPerformanceTimer
timer.StartTimer
.Execute "INSERT INTO TestAB (a_col, b_col) " & _
"SELECT seq, seq " & _
"FROM Sequence " & _
"WHERE seq BETWEEN 1 AND 4200;"
Debug.Print "Sequence = " & timer.GetTimeSeconds
End With
End Sub
在0.2秒内在我的机器上执行!
答案 1 :(得分:2)
更新版本的Access支持@@ IDENTITY变量。您可以使用它在插入后检索标识列,而无需进行查询。
INSERT INTO mytable (field1,field2) VALUES (val1,val2);
SELECT @@IDENTITY;
答案 2 :(得分:0)
我们插入一行,然后再插入一行 查询以检索其自动增量 ID;然后使用此ID插入几个 更多行,将它们链接到第一个
这是一张桌子,两张桌子还是两张以上的桌子?
如果有一张桌子,你可以考虑不同的设计,例如您可以生成自己的随机标识符,使用嵌套集模型而不是邻接列表模型等。很难知道您是否不会共享您的设计; - )
如果两个表(假设FOREIGN KEY
列的表之间有AUTOINCREMENT/IDENTITY
),您可以创建VIEW
INNER JOIN
两个表和{{} 1}} INSERT INTO
和VIEW
值将被'复制'到引用表。此Stack Overflow答案中的更多详细信息和工作示例(链接如下)。
如果有多个表,AUTOINCREMENT/IDENTITY
技巧不会扩展到两个表AFAIK之外,那么您可能不得不忍受性能不佳或改变技术,例如:据报道DAO比ADO更快,SQL Server可能比ACE / Jet更快等等。再次,随意分享您的设计,可能会有一个“开箱即用”的解决方案。
答案 3 :(得分:0)
对于您的情况,最好使用ADO来执行插入而不是运行SQL命令。
for i = 1 to 100
rs.AddNew
rs("fieldOne") = "value1"
rs("fieldOne") = "value2"
rs.Update
id = rs("uniqueIdColumn")
'do stuff with id...
next
我知道使用ADO很慢,但它比打开4200个连接要快许多倍......
答案 4 :(得分:0)
一些提案(甚至可以合并):
答案 5 :(得分:0)
并不意味着成为聪明人......但是,有理由继续使用Access吗? SQL Server Express是免费的,更快速,更强大的......
答案 6 :(得分:0)
听起来您正在导入数据,而Access有更好的设施可用于导入数据。是的,它一次插入许多记录,而且会更快。
你能再描述一下这个应用吗?
答案 7 :(得分:0)
您可以为数据库提供的最便宜且(通常是每次)最佳速度提升是在缓存中保留不变的数据。
不知道它是否适用于你的情况,但它很简单。您可以插入一个(免费)库来执行此操作,或者保留已读取的本地项目集合。示例:
当然,当您运行具有大量服务器的网站时,它可能会稍微复杂一些。在这种情况下,juste使用Application Blocks或Cached。
答案 8 :(得分:0)
请允许我驳斥以下断言:
SELECT @@ IDENTITY建议 - 绝对是一个快得多的方式 插入数据并检索 自动编号值比打开AddOnly 记录集,更新字段,保存 记录和存储自动编号 值。 SQL INSERT将永远是 比逐行使用更快 记录集。
我认为两种记录集方法可能比@@ IDENTITY方法快一点,因为我怀疑它涉及的数据库往返次数较少。在我的测试中,它在1.2秒时更快,而@@ IDENTITY方法则为127秒。这是我的代码(创建.mdb的代码在另一个答案中发布):
Sub TestInerts_rs()
Dim con
Set con = CreateObject("ADODB.Connection")
con.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
Dim timer As CPerformanceTimer
Set timer = New CPerformanceTimer
timer.StartTimer
Dim rs1
Set rs1 = CreateObject("ADODB.Recordset")
With rs1
.ActiveConnection = con
.CursorType = 1 ' keyset
.LockType = 3 ' optimistic
.Source = "SELECT a_col, ID FROM TableA;"
.Open
End With
Dim rs2
Set rs2 = CreateObject("ADODB.Recordset")
With rs2
.ActiveConnection = con
.CursorType = 1 ' keyset
.LockType = 3 ' optimistic
.Source = "SELECT b_col, ID FROM TableB;"
.Open
End With
Dim counter As Long
For counter = 1 To 4200
rs1.AddNew "a_col", counter
Dim identity As Long
identity = rs1.Fields("ID").value
rs2.AddNew Array(0, 1), Array(counter, identity)
Next
Debug.Print "rs = " & timer.GetTimeSeconds
End Sub
答案 9 :(得分:-1)
一些可能有用或可能没有帮助的想法:
让我来看看SELECT @@ IDENTITY建议 - 绝对是一种非常快捷的插入数据和检索自动编号值的方法,而不是打开AddOnly记录集,更新字段,保存记录和存储自动编号值。 SQL INSERT总是比使用逐行记录集更快。
使用DAO事务,您可以将多个此类插入转换为一次执行的批处理。我不确定这一点,因为我不确切知道你在做什么以及你正在使用多少个表。关键是你要在事务中运行一系列SQL INSERT,然后在最后执行.Commit,这样实际写入真实数据库文件将作为单个操作发生,而不是4200(或但是很多)个人操作。