Reputation: 1542
I have an XML file with many records down under "/response/result/record". For example:
<response>
<result>
<record>
<flda>some text</flda>
<fldb>some text</fldb>
: : :
</record>
: : :
</result>
</response>
I have an MSAccess database and a legacy VB6 program that needs to read this file and load it into a table containing a field for each text node in each record.
I know I can use MSXML2.DOMDocument to Load the XML from the internet and possibly save it to a file. I can also then use the DOMDocument to read the XML and load the MSAccess database table one record at a time.
This seems inefficient, since I can load CSV files into an MSAccess table using VB6 syntax such as:
db.Execute "Select * Into CSVtable From [Text;FMT=CSVDelimited;HDR=YES;DATABASE=dirPath].[filename]"
And it will quickly bulk load that data into the database. Much, much faster than reading each record from the CSV file and using a DAO recordset to add each record one at a time.
My question: can I do the same thing with an XML file? What is the syntax and how do I do it?
Follow-up question: Where do I find documentation on this funky 'Select...' syntax that I show above for CSV files (that I originally found on StackOverflow)?
Upvotes: 0
Views: 1315
Reputation: 13267
The SELECT
syntax that you want is defined only in the JETSQL40.CHM document as far as I can determine. This is normally installed with MS Office 2000 or later, but you'll have to dig around in the "Program Files" special folder to find it. There is enough useful stuff there that I usually create a shortcut to it myself.
However since there is no Jet XML Installable ISAM (IISAM) you would have to do the sort of thing that MS Access does itself to import XML format data.
While that does involve looping, you can do your bulk insert in a more optimal manner than people commonly do. The code used below attempts to do that.
This program has a Form with a single Menu item mnuImportXML
and an MSHFlexGrid named FlexGrid
to display the contents of a "Records" table in the database it creates on first run:
Option Explicit
Private Const CONNWG As String = "Provider=Microsoft.Jet.OLEDB.4.0;" _
& "Jet OLEDB:Engine Type=5;" _
& "Jet OLEDB:Create System Database=True;" _
& "Data Source='$DB$.mdw';"
Private Const CONNDB As String = "Provider=Microsoft.Jet.OLEDB.4.0;" _
& "Jet OLEDB:Engine Type=5;" _
& "Jet OLEDB:System Database='$DB$.mdw';" _
& "Data Source='$DB$.mdb';"
Private CN As ADODB.Connection
Private QueryRS As ADODB.Recordset
Private UpdateRS As ADODB.Recordset
Private XmlRS As ADODB.Recordset
Private recordsRS As ADODB.Recordset
Private AppendFields As Variant
Public Function OpenConnection(ByVal DbPath As String) As ADODB.Connection
Dim ExtensionPos As Long
ExtensionPos = InStrRev(DbPath, ".")
If ExtensionPos > 0 Then DbPath = Left$(DbPath, ExtensionPos - 1)
On Error Resume Next
GetAttr DbPath & ".mdb"
If Err Then
On Error GoTo 0
Set OpenConnection = CreateDB(DbPath)
Else
On Error GoTo 0
Set OpenConnection = New ADODB.Connection
OpenConnection.Open Replace$(CONNDB, "$DB$", DbPath)
End If
End Function
Private Function CreateDB(ByVal DbPath As String) As ADODB.Connection
Dim catDB As Object 'Don't early-bind ADOX objects.
Set catDB = CreateObject("ADOX.Catalog")
With catDB
.Create Replace$(CONNWG, "$DB$", DbPath)
.Create Replace$(CONNDB, "$DB$", DbPath)
Set CreateDB = .ActiveConnection
Set catDB = Nothing
End With
With CreateDB
.Execute "CREATE TABLE [Records](" _
& "[ID] IDENTITY CONSTRAINT PK_UID PRIMARY KEY," _
& "[flda] TEXT(255) WITH COMPRESSION," _
& "[fldb] TEXT(255) WITH COMPRESSION)", , _
adCmdText Or adExecuteNoRecords
End With
End Function
Private Sub RefreshGrid()
QueryRS.Open "[Records]", , , adLockReadOnly, adCmdTable
Set FlexGrid.DataSource = QueryRS
QueryRS.Close
End Sub
Private Sub Form_Load()
Set CN = OpenConnection("demo.mdb")
Set QueryRS = New ADODB.Recordset
QueryRS.CursorLocation = adUseClient
Set QueryRS.ActiveConnection = CN
Set XmlRS = New ADODB.Recordset
XmlRS.ActiveConnection = "Provider=MSDAOSP;Data Source=MSXML2.DSOControl.3.0"
Set UpdateRS = New ADODB.Recordset
Set UpdateRS.ActiveConnection = CN
UpdateRS.Properties("Append-Only Rowset").Value = True
AppendFields = Array("flda", "fldb")
RefreshGrid
End Sub
Private Sub Form_Resize()
If WindowState <> vbMinimized Then
FlexGrid.Move 0, 0, ScaleWidth, ScaleHeight
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
CN.Close
End Sub
Private Sub mnuImportXML_Click()
XmlRS.Open "response.xml"
Set recordsRS = XmlRS.Fields("record").Value
UpdateRS.Open "Records", , , adLockOptimistic, adCmdTableDirect
CN.BeginTrans
With recordsRS
Do Until .EOF
UpdateRS.AddNew AppendFields, _
Array(.Fields(AppendFields(0)).Value, _
.Fields(AppendFields(1)).Value)
.MoveNext
Loop
End With
CN.CommitTrans
UpdateRS.Close
Set recordsRS = Nothing
XmlRS.Close
RefreshGrid
End Sub
The key here is the Menu event handler mnuImportXML_Click
(scroll down to the end) which appends the Text of the flda
and fldb
sub-elements of the repeated second-level XML element record
into the Jet database table Records
.
Note the use of:
UpdateRS.Properties("Append-Only Rowset").Value = True
That is done during initialization within Form_Load. It improves performance a bit.
I'm not aware of any quicker way to accomplish this. Jet can't use an XML document as an external database. It can only make use of database types that it has an IISAM for such as Text, HTML, Excel 8.0, dBase IV, and Paradox.
Whether Microsoft was just lazy and left XML out of Jet 4.0 without thinking, the hierarchical nature of XML documents didn't leave them many options without extending Jet SQL syntax, or there is some syntax I haven't found... I just can't say.
Upvotes: 1