Некоторые люди написали, что @@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 секунды!
person
onedaywhen
schedule
08.05.2009