この状況は、AccessでODBCリンクテーブルへの一括INSERTを処理する場合に珍しいことではありません。次のアクセスクエリの場合
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
ここで、[METER_DATA]はODBCリンクテーブルであり、[tblTempSmartSSP]はローカル(ネイティブ)アクセステーブルです。ODBCは、機能が異なる可能性のあるさまざまなターゲットデータベースに対応できる必要があるため、その巧妙さには多少の制限があります。大きく。残念ながら、多くの場合、単一のAccess SQLステートメントにもかかわらず、リモート(リンクされた)データベースに実際に送信されるのは、ローカルテーブルの各行に対して個別のINSERT(または同等のもの)であるということです。 。当然のことながら、ローカルテーブルに多数の行が含まれている場合、これは非常に遅くなる可能性があります。
オプション1:リモートデータベースへのネイティブ一括挿入
すべてのデータベースには、データの一括読み込みのための1つ以上のネイティブメカニズムがあります。MicrosoftSQLServerには「bcp」とBULK INSERT
があります。 、およびOracleには「SQL*Loader」があります。これらのメカニズムは一括操作用に最適化されており、通常、速度が大幅に向上します。実際、リモートデータベースに転送する前にデータをAccessにインポートして「マッサージ」する必要がある場合でも、変更したデータをテキストファイルにダンプして戻し、リモートデータベースに一括インポートする方が高速です。
オプション2:Accessでパススルークエリを使用する
一括インポートメカニズムが実行可能なオプションでない場合、別の可能性は、Accessで1つ以上のパススルークエリを作成し、一度に複数の行を挿入できるINSERTステートメントを使用してデータをアップロードすることです。
たとえば、リモートデータベースがSQL Server(2008以降)の場合、次のようなアクセスパススルー(T-SQL)クエリを実行できます
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
1つのINSERTステートメントで3つの行を挿入します。
ここでの別の以前の質問への回答によると、Oracleに対応する構文は次のようになります
INSERT ALL
INTO METER_DATA (MPO_REFERENCE) VALUES (1)
INTO METER_DATA (MPO_REFERENCE) VALUES (2)
INTO METER_DATA (MPO_REFERENCE) VALUES (3)
SELECT * FROM DUAL;
10,000行のネイティブ[tblTempSmartSSP]テーブルを使用して、SQL Serverでこのアプローチをテストしました(Oracleデータベースにアクセスできないため)。コード...
Sub LinkedTableTest()
Dim cdb As DAO.Database
Dim t0 As Single
t0 = Timer
Set cdb = CurrentDb
cdb.Execute _
"INSERT INTO METER_DATA (MPO_REFERENCE) " & _
"SELECT MPO_REFERENCE FROM tblTempSmartSSP", _
dbFailOnError
Set cdb = Nothing
Debug.Print "Elapsed time " & Format(Timer - t0, "0.0") & " seconds."
End Sub
...私のテスト環境で実行するのに約100秒かかりました。
対照的に、上記のように複数行のINSERTを構築する次のコード(Microsoftがテーブル値コンストラクターと呼んでいるものを使用)...
Sub PtqTest()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim t0 As Single, i As Long, valueList As String, separator As String
t0 = Timer
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT MPO_REFERENCE FROM tblTempSmartSSP", dbOpenSnapshot)
i = 0
valueList = ""
separator = ""
Do Until rst.EOF
i = i + 1
valueList = valueList & separator & "(" & rst!MPO_REFERENCE & ")"
If i = 1 Then
separator = ","
End If
If i = 1000 Then
SendInsert valueList
i = 0
valueList = ""
separator = ""
End If
rst.MoveNext
Loop
If i > 0 Then
SendInsert valueList
End If
rst.Close
Set rst = Nothing
Set cdb = Nothing
Debug.Print "Elapsed time " & Format(Timer - t0, "0.0") & " seconds."
End Sub
Sub SendInsert(valueList As String)
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = cdb.TableDefs("METER_DATA").Connect
qdf.ReturnsRecords = False
qdf.sql = "INSERT INTO METER_DATA (MPO_REFERENCE) VALUES " & valueList
qdf.Execute dbFailOnError
Set qdf = Nothing
Set cdb = Nothing
End Sub
...同じ結果が得られるまでに1〜2秒かかりました。
(T-SQLテーブル値コンストラクターは一度に1000行を挿入するように制限されているため、上記のコードはそれ以外の場合よりも少し複雑です。)