{"id":2009,"date":"2015-06-02T00:00:00","date_gmt":"2015-06-02T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/sqlxml-bulk-loader-basics\/"},"modified":"2021-05-11T15:57:20","modified_gmt":"2021-05-11T15:57:20","slug":"sqlxml-bulk-loader-basics","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/tools-sql-server\/sqlxml-bulk-loader-basics\/","title":{"rendered":"SQLXML Bulk Loader Basics"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"MsoNormal\">At some point in their careers, most SQL Server developers will have faced the  challenge of getting the data from an XML source file into SQL Server. Despite the ubiquity of the CSV format, and the  rising star of JSON, a vast quantity of data is still exchanged as XML files.<\/p>\n<p class=\"MsoNormal\">For bulk loading XML data, the standard tools in the Extract-Transform-Load  (ETL) toolkit tend to be the venerable OPENXML, or slightly more modern OPENROWSET\/XQUERY, for the T-SQL purist, or for  SSIS devotees the XML task.<\/p>\n<p class=\"MsoNormal\">Any of these solutions work well enough for small-to-medium sized files and even  quite complex XML structures, but they often don&#8217;t scale to larger loads. Anything over 2GB will crash OPENXML or SSIS  in my experience. In some cases, even if they can load a large file, the time taken could prove a threat to your SLAs.<\/p>\n<p class=\"MsoNormal\">In these circumstances, SQLXML Bulk Loader is your friend. It can import  extremely large XML source data files into SQL Server at amazing speed, while preserving referential integrity, if  required. Unfortunately, it&#8217;s not installed with SQL Server, by default, and it&#8217;s poorly explained in the SQL Server  documentation. As a result, most developers either ignore it completely, or implement it poorly and it is unfairly  perceived as &#8220;hard to use&#8221;. <\/p>\n<p class=\"MsoNormal\">This article attempts to put the record straight. It will describe briefly how  to find and install SQLXML Bulk Loader and then how to use it to load three &#8220;classic&#8221; XML file structures:<\/p>\n<ol>\n<li>A simple XML format<\/li>\n<li>Multiple tables stored as XML<\/li>\n<li>A nested, moderately complex XML structure that maps to a relational  table structure<\/li>\n<\/ol>\n<p class=\"MsoNormal\">To work through the examples, you&#8217;ll need to download the sample XML files from  the &#8220;article rating&#8221; box at the top of this article. I&#8217;ve provided the kind of &#8220;semi-structured&#8221; source files that  SQLXML Bulk Loader can digest with ease. You&#8217;ll also need to create a database in which to practice. In the examples,  I&#8217;ve assumed you&#8217;ve created a directory called <code>  C:\\SQLXMLArticle<\/code> for the files, and created a database called  <code> CarSales_Staging<\/code>. All  techniques described will work with all versions of the database from 2005 through 2014.<\/p>\n<p class=\"MsoNormal\">In a subsequent article, I will explain some of the more advanced features of  SQLXML Bulk Loader, from handling attribute-centric XML to optimizing the process for bulk loads via validating the XML  and integrating SQLXML Bulk Loader with SSIS, and other tricks and techniques to help you get the most out of this tool.<\/p>\n<h2>SQLXML Bulk Loader Use Cases<\/h2>\n<p class=\"MsoNormal\">SQLXML Bulk Loader is best used in the following situations:<\/p>\n<ul>\n<li>When the source XML file is large.  What is large? Well, if you were planning on using OPENXML, then that means any file over 2 gigabytes. The same upper  limit is true of XML loaded into a variable and shredded using XQuery. With SSIS, it depends on the memory available,  but I have experienced practical difficulties way below the 2 GB limit. In contrast, I have loaded XML files of tens of  gigabytes using SQLXML Bulk Load.<\/li>\n<li>When the XML source structure is relatively  simple. In reality, this means a file consisting of tables and fields where the nested  XML structure is not too complex. The source data cannot be too intricate, in XML terms, or it will not load. It is not  for nothing that this is called &#8220;semi-structured&#8221; XML data.<\/li>\n<li>When you wish to load multiple tables  from the same source file.<\/li>\n<li>When speed loading the data is important.  In my tests, SQLXML Bulk Load loaded data at about 90 percent of native BCP speeds, for separate tables without  relational links.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">Be aware from the start that SQLXML Bulk Loader can only handle a certain level  of complexity in the source file. Should you be faced with a file that SQLXML Bulk Loader refuses to load, your only  solution will be one of the following:<\/p>\n<ul>\n<li>Negotiate a simpler structure from the data supplier<\/li>\n<li>Use one of the other available technologies mentioned above<\/li>\n<li>Use XSLT to simplify or &#8216;flatten&#8217; the file structure, to produce a  file amenable to processing by SQLXML. XML transformation is a vast and completely separate topic and one that I will  not be explaining it in this article<\/li>\n<\/ul>\n<h2>Preparing SQLXML Bulk Loader<\/h2>\n<p class=\"MsoNormal\">SQLXML 4.0 was installed by default as far as SQL Server 2005 but from SQL  Server 2008 onwards, it has to be installed as part of the Feature Pack, for SQL Server 2008, or downloaded separately.  You can obtain version 4.0 SP1 at the following URL:<\/p>\n<p class=\"MsoNormal\"><a href=\"http:\/\/www.microsoft.com\/en-gb\/download\/details.aspx?id=30403\"> http:\/\/www.microsoft.com\/en-gb\/download\/details.aspx?id=30403<\/a><\/p>\n<p class=\"MsoNormal\">SQLXML Bulk Loader exists in both 32- and 64-bit versions (there is even a  64-bit IA version, but I expect that few people will be needing this). The installation is simple and I will not waste  your time explaining it here. Once complete, you will be ready to start using SQLXML Bulk Loader to load XML files into  SQL Server.<\/p>\n<h2>Importing an XML File into SQL Server<\/h2>\n<p class=\"MsoNormal\">SQLXML Bulk Loader is a COM (Component Object Model) object that allows you to  load semi-structured XML data into SQL Server tables. To make it work, you will need the following:<\/p>\n<ul>\n<li>An XML source file.<\/li>\n<li>A destination table in a SQL Server database.<\/li>\n<li>An XSD schema file that maps the XML to the destination table &#8211; this  is the interesting part of the process<\/li>\n<li>A Visual Basic Script file to execute the XML load process.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">Listing 1 shows the XML source, which in this simple  example will be used to &#8220;bulk import&#8221; 2 rows in a SQL Server table. Throughout the article, the code listing caption  provides the name of the relevant file in the code download bundle.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&lt;?xml version = \"1.0\" encoding = \"UTF-8\"?&gt;\n\t&lt;CarSales&gt;\n\t&#160;&lt;Client&gt;\n\t&#160; &lt;ID &gt;3&lt;\/ID&gt;\n\t&#160; &lt;ClientName&gt;John Smith&lt;\/ClientName&gt;\n\t&#160; &lt;Address1&gt;4, Grove Drive&lt;\/Address1&gt;\n\t&#160; &lt;Town&gt;Uttoxeter&lt;\/Town&gt;\n\t&#160; &lt;County&gt;Staffs&lt;\/County&gt;\n\t&#160; &lt;Country&gt;1&lt;\/Country&gt;\n\t&#160;&lt;\/Client&gt;\n\t&#160;&lt;Client&gt;\n\t&#160; &lt;ID&gt;7&lt;\/ID&gt;\n\t&#160; &lt;ClientName&gt;Slow Sid&lt;\/ClientName&gt;\n\t&#160; &lt;Address1&gt;2, Rue des Bleues&lt;\/Address1&gt;\n\t&#160; &lt;Town&gt;Avignon&lt;\/Town&gt;\n\t&#160; &lt;County&gt;Vaucluse&lt;\/County&gt;\n\t&#160; &lt;Country&gt;3&lt;\/Country&gt;\n\t&#160;&lt;\/Client&gt;\n\t&lt;\/CarSales&gt;\n<\/pre>\n<p class=\"caption\">Listing 1: Clients_Simple.Xml<\/p>\n<p class=\"MsoNormal\">An XML document is checked for being well-formed, but it is not validated. If  the XML document is not well-formed, processing is cancelled.<\/p>\n<p class=\"MsoNormal\">This example uses XML elements, but attribute-centric XML source data can also  be loaded with SQLXML Bulk Loader. I give a short example of this in a subsequent article.<\/p>\n<div class=\"STTipsHeader note\">\n<p class=\"note\"><strong>Inline schemas are not supported<\/strong><strong><\/strong><br \/>If you have an inline schema in the <br \/>source XML document, SQLXML will ignore it.  <\/p>\n<\/div>\n<p class=\"MsoNormal\">Listing 2 creates the SQL Server table, the  destination for this sample data load.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tCREATE TABLE CarSales_Staging.dbo.Client_XMLBulkLoad\n\t&#160;&#160;&#160;  (\n\t&#160;&#160;&#160;&#160;&#160;  ID INT NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  ClientName NVARCHAR(1000) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  Address1 NVARCHAR(1000) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  Town NVARCHAR(1000) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  County NVARCHAR(1000) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  Country NUMERIC(18, 0) NULL\n\t&#160;&#160;&#160; );\n\tGO\n<\/pre>\n<p class=\"caption\">Listing 2: Client_XMLBulkLoad.sql<\/p>\n<p class=\"MsoNormal\">Listing 3 shows the XSD schema file that maps the  source XML to the destination table. I will explain its workings once the data is loaded.<\/p>\n<pre>\t&lt;xsd:schema xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:sql = \"urn:schemas-microsoft-com:mapping-schema\"&gt;\n\t&#160;&lt;xsd:element name = \"\"CarSales\"\" sql:is-constant = \"1\" &gt;\n\t&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160; &lt;xsd:element name = \"Client\" sql:relation = \"Client_XMLBulkLoad\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; maxOccurs = \"unbounded\"&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ID\" type = \"xsd:integer\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"ID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ClientName\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"ClientName\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Address1\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"Address1\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Town\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"\"Town\"\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"County\"\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"County\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Country\" type = \"xsd:decimal\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"Country\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160; &lt;\/xsd:element&gt;\n\t&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160; &lt;\/xsd:element&gt;\n\t&lt;\/xsd:schema&gt;\n<\/pre>\n<p class=\"caption\">Listing 3: SQLXMLBulkLoadImport_Simple.xsd<\/p>\n<p class=\"MsoNormal\">Finally, Listing 4 shows the VBScript file used to  invoke SQLXML Bulk Load and load the data.<\/p>\n<pre>\tSet objBL = CreateObject(\"SQLXMLBulkLoad.SQLXMLBulkload.4.0\")\n\tobjBL.ConnectionString = \"provider = SQLOLEDB;data source=MySQLServer;database=CarSales_Staging; integrated security = SSPI\"\n\tobjBL.ErrorLogFile = \"C:\\SQLXMLArticle\\SQLXMLBulkLoadImporterror.log\"\n\tobjBL.Execute \"C:\\SQLXMLArticle\\SQLXMLBulkLoadImport_Simple.xsd\",\"C:\\SQLXMLArticle\\Clients_Simple.xml\"\n\tSet objBL = Nothing\n<\/pre>\n<p class=\"caption\">Listing 4: SQLXMLBulkload.vbs<\/p>\n<p>&#8230;or in PowerShell &#8230;<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t$objBL = new-object -comobject 'SQLXMLBulkLoad.SQLXMLBulkLoad'\n\t$objBL.ConnectionString = 'provider = SQLOLEDB;data source=YourServer;database=CarSales_Staging; integrated security = SSPI'\n\t$objBL.ErrorLogFile ='C:\\SQLXMLArticle\\SQLXMLBulkLoadImporterror.log'\n\t$objBL.Execute('C:\\SQLXMLArticle\\SQLXMLBulkLoadImport_Simple.xsd','C:\\SQLXMLArticle\\Clients_Simple.xml')\n\t$objBL = $null&#160; \n<\/pre>\n<p class=\"caption\">Listing  5: SQLXMLBulkload.ps1<\/p>\n<p class=\"MsoNormal\">If the .xml and .xsd  files are in the same directory as the .vbs file then you will not need to add the full  paths after the <code> .Execute<\/code>  command in the VBScript file. However it is probably a good habit to use full path details, as a matter of principle.<\/p>\n<p class=\"MsoNormal\">Double-click the SQLXMLBulkload.vbs file to run the  bulk load. If all goes well, you should be able to query the Client_XMLBulkLoad table and  see the data from the XML source file correctly loaded.<\/p>\n<h3>A Closer Look at the Schema File<\/h3>\n<p class=\"MsoNormal\">The schema (XSD) file contains a few extra tidbits, over and above the Microsoft  mapping schema, that allow it to perform its job so efficiently. Essentially, it extends the basic schema with the  attributes that allow SQLXML to channel the source data into the correct tables and fields.<\/p>\n<p class=\"MsoNormal\">Perhaps the easiest way to understand what is special about the XSD that SQLXML  bulk loader uses is to compare it with a &#8220;plain vanilla&#8221; XSD for the same XML source file. Listing  6 shows the XSD  without the extensions that SQLXML bulk loader needs.<\/p>\n<pre>\t&lt;xsd:schema xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\"&gt;\n\t&lt;xsd:element name = \"CarSales\"&gt;\n\t&#160;&lt;xsd:complexType&gt;\n\t&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160; &lt;xsd:element name = \"Client\"&gt;\n\t&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ID\" type = \"xsd:integer\"\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ClientName\" type = \"xsd:string\"\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Address1\" type = \"xsd:string\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Town\" type = \"xsd:string\"\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"County\" type = \"xsd:string\"\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Country\" type = \"xsd:decimal\"\/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&#160;&#160; &lt;\/xsd:element&gt;\n\t&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&lt;\/xsd:element&gt;\n\t&lt;\/xsd:schema&gt;\n<\/pre>\n<p class=\"caption\">Listing 6: Vanilla.XSD file<\/p>\n<p class=\"MsoNormal\">As you can see this version is much simpler. So what was added to the basic  schema to make the data load work? Only four annotations were necessary to apply all the required constraints on the  data. They are:<\/p>\n<ul>\n<li><code>xmlns:sql = \"urn:schemas-microsoft-com:mapping-schema\"<\/code> &#8211; This extension to the  namespace specifies how the schema is extended.<\/li>\n<li><code>Sql:relation<\/code> &#8211;  \t\tThis extension indicates to SQLXML the \t\t<b>table<\/b> into which the data is to be loaded for this XML element. As you can  see from Listing 3, this is added to the <code> \t\t&lt;Client&gt;<\/code>  element to indicate that this particular element maps to the  \t\tClient_XMLBulkLoad  table in the SQL Server database.<\/li>\n<li><code>sql:field<\/code> &#8211;  \t\tThis extension indicates to SQLXML the \t\t<b>field<\/b> in the destination table into which the data is loaded. This is added  to each of the nested elements in the <code> \t\t&lt;Client&gt;<\/code> element to indicate  which field the XML element is mapped to in the Client_XMLBulkLoad table in SQL Server  \t\t \t\t \t\t<\/li>\n<li><code>sql:is-constant = \"1\"<\/code> &#8211; This extension tells SQLXML that the  \t\t<code>&lt;CarSales&gt;<\/code> element is a \t\t<b>container<\/b> and not a data element &#8211; and so does \t\t<b>not<\/b> need to be loaded into a data table. This annotation has to be added to  all &#8220;parent container&#8221; elements.  \t\t \t\t<\/li>\n<\/ul>\n<p class=\"MsoNormal\">Getting the schema file right is the hard part of using SQLXML bulk loader. You  will probably spend more time on crafting this file than with any other part of the SQLXML bulk load process.  Consequently it is worth ensuring that you have understood the XSD extensions before attempting a complex data load. You  may even find that practicing on a simple XML file to start with can reap dividends. You can then move on to tackling a  large and more complex XML source file once you are confident that you have mastered the basics.<\/p>\n<p class=\"MsoNormal\">However, you do not need to hand-craft the entire schema file from scratch.  There are many tools available that can analyze an XML file and produce a basic XSD file that you can then extend by  adding the Microsoft mapping extensions. One tool that is available with any Visual Studio installation is  <code>  xsd.exe<\/code>. It will take an XML source file and return a schema file in all but the most  arcane cases. You can find out more about this at: <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/x6c1kb0s(VS.80).aspx\"><span class=\"STHyperlink\"> XML Schema Definition Tool (Xsd.exe)<\/span><\/a>.  If this tool does not meet your needs, then you can try a PowerShell alternative that is described at: <a href=\"http:\/\/www.sqlservercentral.com\/articles\/powershell\/65196\/\"><span class=\"STHyperlink\"> Loading Data With PowerShell<\/span><\/a>.<\/p>\n<h3>How the VBScript File Works<\/h3>\n<p class=\"MsoNormal\">The .vbs file that invokes SQLXML Bulk Loader can be  extremely simple, and only needs the following items:<\/p>\n<ul>\n<li>The object creation statement &#8211; to invoke  the SQLXML Bulk Load COM object.<\/li>\n<li>A standard OLEDB connection string,  containing, at a minimum:\n<ul>\n<li>The server and, if required, instance name (<code>Data  Source<\/code> in this example).<\/li>\n<li>The destination database name (<code>CarSales_Staging<\/code> in this  example).<\/li>\n<li>The SQL Server security information (Windows integrated security or  SQL Server security).<\/li>\n<\/ul>\n<\/li>\n<li>The <code> \t\tExecute<\/code> command, which provides  paths to the XSD and XML files &#8211; in that order.<\/li>\n<li>An ErrorLog file. While not compulsory,  this is invaluable for debugging the process.<\/li>\n<li>A <code>SET<\/code> command to dispose of the  COM object.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">This simple VB script assumes that the destination table already exists  (although you can create the table as part of the load as you can see in a companion article). It also presumes  integrated security. It is interesting that, somewhat counter-intuitively, the XML schema (.xsd)  file is passed as the first parameter to the XML Bulk Loader. The XML file itself is passed in as the second parameter.<\/p>\n<h3>Troubleshooting<\/h3>\n<p class=\"MsoNormal\">Two things will tell you that the XML data load has worked:<\/p>\n<ul>\n<li>There is no ErrorLog file, or if the old ErrorLog file is removed  (assuming that you have requested a log file)<\/li>\n<li>The fact that the data loaded correctly into the destination table(s)<\/li>\n<\/ul>\n<p class=\"MsoNormal\">Should the data not load, then your first port of call is the ErrorLog file that  SQLXML Bulk Load created, assuming that you added the <code>  objBL.ErrorLogFile = \"Log File and path\"<\/code> parameter. Fortunately, these error log files  are very explicit, and always prove to be an invaluable source of debugging information if you encounter load issues.<\/p>\n<p class=\"MsoNormal\">There are a few precautions that you can take to prevent persistent recourse to  the error log file becoming a way of life. Nearly all your attention should probably focus on the schema file. In my  experience, this is the most common source of problems, because it generally involves a certain amount of hand-crafting.  Potential problems include:<\/p>\n<ul>\n<li>Making sure that there are no spaces immediately inside all the  double-quotes that are used in the <code> \t\txsd:element<\/code> definition.<\/li>\n<li>Ensuring that you do not inadvertently close elements such as  \t\t<code>xsd:sequence<\/code> and  \t\t<code>xsd:complextype<\/code> &#8211; or even some  of the <code>xsd:element<\/code> definitions.<\/li>\n<li>Respecting case-sensitivity. So in the preceding example, having an  element called &#8220;Country&#8221;, and a schema mapping element named  \t\t&#8220;country&#8221;  would cause the process to fail.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">An interesting &#8220;feature&#8221; concerns the VBScript file. You have to be sure that  you do not leave any spaces in the <code> database=DatabaseName<\/code> part of  the script file. Spaces here, even if they are allowed elsewhere in the connection string, will cause a cryptic error  message complaining that the table object cannot be found.<\/p>\n<h2>Loading Multiple Tables at Once from a Single XML Source File<\/h2>\n<p class=\"MsoNormal\">One advantage that an XML file can have over a CSV file is the capacity to store  multiple unrelated tables in a single source file. There will doubtless be occasions when you will want to load data  from an XML source file that contains data for several separate tables. With the SQLXML Bulk Load executable and a  suitably-crafted XSD file, you can load data into multiple tables from a single XML file in a single process. SQLXML  Bulk Loader allows you, in effect, to transfer an entire subset of data in a single file, ready to be loaded  simultaneously into multiple tables in a destination database. The data load can also be blindingly fast.<\/p>\n<p class=\"MsoNormal\">This technique is predicated on the fact that the source XML is very simple in  layout. It also presumes that you are essentially loading data from a &#8220;table dump&#8221; in an XML file, where each table is  stored sequentially in the XML file. You are also nearly always best advised to drop any indexes for the duration of the  load, and recreate them afterwards when loading multiple tables. Moreover it is also best to disable foreign key  constraints during the data load and reapply them afterwards.<\/p>\n<p class=\"MsoNormal\">To emulate the &#8220;table dump&#8221; scenario, the data file used in this example, shown  in Listing 7, contains two tables in XML format, <code>  Invoice<\/code> and <code>  Invoice_Lines<\/code>.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&lt;?xml version = \"1.0\" encoding = \"UTF-8\" ?&gt;\n\t&lt;ROOT&gt;\n\t&#160;&lt;Invoice&gt;\n\t&#160; &lt;ID&gt; 3&lt;\/ID&gt;\n\t&#160; &lt;InvoiceNumber&gt; AA\/1\/2014-07-25&lt;\/InvoiceNumber&gt;\n\t&#160; &lt;DeliveryCharge&gt; 250&lt;\/DeliveryCharge&gt;\n\t&#160;&lt;\/Invoice&gt;\n\t&#160;&lt;Invoice_Lines&gt;\n\t&#160; &lt;InvoiceID&gt; 3&lt;\/InvoiceID&gt;\n\t&#160; &lt;SalePrice&gt; 5000&lt;\/SalePrice&gt;\n\t&#160; &lt;\/Invoice_Lines&gt;\n\t&#160;&lt;Invoice_Lines&gt;\n\t&#160; &lt;InvoiceID&gt;3&lt;\/InvoiceID&gt;\n\t&#160; &lt;SalePrice&gt; 12500&lt;\/SalePrice&gt;\n\t&#160;&lt;\/Invoice_Lines&gt;\n\t&lt;\/ROOT&gt;\n<\/pre>\n<p class=\"caption\">Listing 7: SQLXMLSourceDataMultipleTables.xml<\/p>\n<p class=\"MsoNormal\">The file only has one record for <code> Invoice<\/code> and two for  <code>  Invoice_Lines<\/code>, but in reality there could be dozens of tables, each containing millions  of records. Note that there is no presumption in the XML of any relational link between the  <code>  Invoice<\/code> and <code>  Invoice_Lines<\/code> elements.<\/p>\n<p class=\"MsoNormal\">Listing 8 shows the XSD file that maps the source XML data to two tables in a  SQL Server database, which are also called <code> Invoice<\/code> and  <code>  Invoice_Lines<\/code>.<\/p>\n<pre>\t&lt;xsd:schema xmlns:xsd = \"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:sql = \"urn:schemas-microsoft-com:mapping-schema\"&gt;\n\t&#160;&lt;xsd:element name = \"ROOT\" sql:is-constant = \"1\" &gt;\n\t&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160; &lt;!-- Invoice --&gt;\n\t&#160;&#160;&#160; &lt;xsd:element name = \"Invoice\" sql:relation = \"Invoice\"&gt;\n\t&#160;&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ID\" type = \"xsd:integer\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"ID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"InvoiceNumber\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"InvoiceNumber\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"DeliveryCharge\" type = \"xsd:decimal\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"DeliveryCharge\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&#160;&#160; &lt;\/xsd:element&gt;\n\t&#160;&#160; &lt;!-- Invoice Lines --&gt;\n\t&#160;&#160; &lt;xsd:element name = \"Invoice_Lines\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:relation = \"Invoice_Lines\" &gt;\n\t&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"InvoiceID\" type = \"xsd:integer\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"InvoiceID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"SalePrice\" type = \"xsd:string\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"SalePrice\" \/&gt;\n\t&#160;&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&#160; &lt;\/xsd:element&gt;\n\t&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&lt;\/xsd:complexType&gt;\n\t&lt;\/xsd:element&gt;\n\t&lt;\/xsd:schema&gt;\n<\/pre>\n<p class=\"caption\">Listing 8: SQLXMLBulkLoadImportMultipleTables.xsd<\/p>\n<p class=\"MsoNormal\">This schema file uses the same extensions as the previous one that you saw, only  this time there are two &#8220;tables&#8221; of data &#8211; each mapped using the <code> sql:relation<\/code> annotation. Each of the XML elements  containing data inside the XML container element is mapped to the corresponding database field with the  <code> sql:field<\/code> annotation.<\/p>\n<p class=\"MsoNormal\">As you can see, each XML element that maps to a table is a separate XML node,  and the order of the elements is unimportant. What is important, however, is to have a single root node, just as you  would for a conventionally well-formed XML file.<\/p>\n<p class=\"MsoNormal\">The only potential issue with this approach is that the XSD file can become  extremely complex if you have to handle dozens of tables where each one can have potentially hundreds of columns. It is  not that the XSD file is necessarily difficult to create, just arduous. So arm yourself with patience, and be prepared  for some repetitive testing.<\/p>\n<p class=\"MsoNormal\">The penultimate stage is to create the destination tables, as shown in Listing  8.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tCREATE TABLE CarSales_Staging.dbo.Invoice\n\t&#160;&#160;&#160;  (\n\t&#160;&#160;&#160;&#160;&#160;  ID INT NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  InvoiceNumber VARCHAR(50) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  DeliveryCharge SMALLMONEY NULL\n\t&#160;&#160;&#160; );\n\tGO\n\tCREATE TABLE CarSales_Staging.dbo.Invoice_Lines\n\t&#160;&#160;&#160;  (\n\t&#160;&#160;&#160;&#160;&#160;  InvoiceID INT NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  SalePrice MONEY NULL,\n\t&#160;&#160;&#160; );\n\tGO\n<\/pre>\n<p class=\"caption\">Listing 9: tblInvoiceMulti.Sql<\/p>\n<p class=\"MsoNormal\">Once the destination tables exist, you can create and execute a VBScript file  just as you did before. Here is the script to use.<\/p>\n<pre>\tSet objBL = CreateObject(\"SQLXMLBulkLoad.SQLXMLBulkload.4.0\")\n\tobjBL.ConnectionString = \"provider = SQLOLEDB;data source=YourServer;\n\tdatabase=CarSales_Staging; integrated security = SSPI\"\n\tobjBL.ErrorLogFile = \"C:\\SQLXMLArticle\\SQLXMLBulkLoadImporterror.log\"\n\tobjBL.Execute \"C:\\SQLXMLArticle\\SQLXMLBulkLoadImportMultipleTables.xsd\", \n\t\"C:\\SQLXMLArticle\\SQLXMLSourceDataMultipleTables.xml\"\n\tSet objBL = Nothing\n<\/pre>\n<p class=\"caption\">Listing 10: SQLXMLBulkloadMulti.vbs<\/p>\n<p>&#8230; or in PowerShell<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t$objBL = new-object -comobject 'SQLXMLBulkLoad.SQLXMLBulkLoad'\n\t$objBL.ConnectionString = 'provider = SQLOLEDB;data source=YourServer;database=CarSales_Staging; integrated security = SSPI'\n\t$objBL.ErrorLogFile ='C:\\SQLXMLArticle\\SQLXMLBulkLoadImporterror.log'\n\t$objBL.Execute('C:\\SQLXMLArticle\\SQLXMLBulkLoadImportMultipleTables.xsd', `\n&#160; &#160; &#160; &#160; &#160; &#160; &#160; 'C:\\SQLXMLArticle\\SQLXMLSourceDataMultipleTables.xml')\n\t$objBL = $null&#160; \n<\/pre>\n<p class=\"caption\">Listing 11: SQLXMLBulkloadMulti.ps1<\/p>\n<p class=\"MsoNormal\">Once again the source data is loaded into the destination database, this time  loading multiple tables simultaneously, from a single file in a single operation. <\/p>\n<p class=\"MsoNormal\">There is nothing to stop you from enforcing referential integrity through the  addition of primary and foreign key constraints once the load has finished, so this technique can become a method for  transferring data between databases. Certainly, it is not as simple as a backup and restore, but it can prove useful in  certain circumstances, such as when:<\/p>\n<ul>\n<li>You need to transfer data between multiple versions of SQL server, and  frequently into older versions of the database.<\/li>\n<li>The environment that you are using does not allow for direct  connections between databases.<\/li>\n<li>CSV files prove unreliable for the data that you are using.<\/li>\n<li>You need to use the same data for loading into multiple different  databases (Oracle, MySQL or DB2 for instance) &#8211; and XML is the most appropriate &#8220;Lingua Franca&#8221;.<\/li>\n<li>You need a rapid and reliable data load process.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">On another note, it is worth remarking that the XML data file can contain  more &#8220;tables&#8221; and &#8220;records&#8221; than are mapped in the schema file. However, only those entities that are specified in the  XSD file will be loaded.<\/p>\n<h2>Loading and Shredding Relational Tables from an XML Source File<\/h2>\n<p class=\"MsoNormal\">XML is nothing if not infinitely varied. Frequently, the XML source file that  will be more intricate than the simple &#8220;table dump&#8221; structure of the previous example. Often, the data that makes up  several tables is presented in a hierarchical way inside a single XML element.<\/p>\n<p class=\"MsoNormal\">Listing 12 illustrates a simple version of this nested XML pattern.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&lt;?xml version = \"1.0\" encoding = \"UTF-8\" ?&gt;\n\t&lt;ROOT&gt;\n\t&#160;&lt;Invoice&gt;\n\t&#160; &lt;ID &gt;3&lt;\/ID&gt;\n\t&#160; &lt;InvoiceNumber &gt;3A9271EA-FC76-4281-A1ED-714060ADBA30&lt;\/InvoiceNumber&gt;\n\t&#160; &lt;DeliveryCharge &gt;250&lt;\/DeliveryCharge&gt;\n\t&#160;&#160; &lt;Invoice_Lines&gt;\n\t&#160;&#160;&#160; &lt;ID &gt;1&lt;\/ID&gt;\n\t&#160;&#160;&#160; &lt;InvoiceID &gt;3&lt;\/InvoiceID&gt;\n\t&#160;&#160;&#160; &lt;SalePrice &gt;5000&lt;\/SalePrice&gt;\n\t&#160;&#160; &lt;\/Invoice_Lines&gt;\n\t&#160; &lt;Invoice_Lines&gt;\n\t&#160;&#160; &lt;ID &gt;2&lt;\/ID&gt;\n\t&#160;&#160; &lt;InvoiceID &gt;5&lt;\/InvoiceID&gt;\n\t&#160;&#160; &lt;SalePrice &gt;12500&lt;\/SalePrice&gt;\n\t&#160; &lt;\/Invoice_Lines&gt;\n\t&#160;&lt;\/Invoice&gt;\n\t&lt;\/ROOT&gt;\n<\/pre>\n<p class=\"caption\">Listing 12: SQLXMLSourceDataRelatedTables.xml<\/p>\n<p class=\"MsoNormal\">Although this file only contains a small amount of data it maps to a classic  Invoice header \/ Invoice Item pattern, where the <code> &lt;Invoice&gt;<\/code>  element contains the <code> &lt;Invoice_Lines&gt;<\/code> element. We can load this kind of data  directly into a relational table structure using SQLXML Bulk Loader.<\/p>\n<p class=\"MsoNormal\">Drop the two tables used in the previous example and re-create them with a <code>  FOREIGN KEY<\/code> constraint, as shown in Listing 11.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\tDROP TABLE CarSales_Staging.dbo.Invoice;\n\tDROP TABLE CarSales_Staging.dbo.Invoice_Lines;\n\tCREATE TABLE CarSales_Staging.dbo.Invoice\n\t&#160;&#160;&#160;  (\n\t&#160;&#160;&#160;&#160;&#160;  ID INT NOT NULL\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PRIMARY KEY ,\n\t&#160;&#160;&#160;&#160;&#160;  InvoiceNumber VARCHAR(50) NULL ,\n\t&#160;&#160;&#160;&#160;&#160;  DeliveryCharge SMALLMONEY NULL\n\t&#160;&#160;&#160; );\n\tGO\n\tCREATE TABLE CarSales_Staging.dbo.Invoice_Lines\n\t&#160;&#160;&#160;  (\n\t&#160;&#160;&#160;&#160;&#160;  ID INT NOT NULL\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PRIMARY KEY ,\n\t&#160;&#160;&#160;&#160;&#160;  InvoiceID INT NOT NULL\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; FOREIGN KEY REFERENCES Invoice ( ID ) ,\n\t&#160;&#160;&#160;&#160;&#160;  SalePrice MONEY NULL,\n\t&#160;&#160;&#160; );\n\tGO\n<\/pre>\n<p class=\"caption\">Listing 13: LoadRelationalXML.sql<\/p>\n<p class=\"MsoNormal\">Create an appropriate XML schema, as shown in Listing 12 (I&#8217;ll explain this file  in a little more detail shortly).<\/p>\n<pre>\t&lt;xsd:schema xmlns:xsd = \"http:\/\/www.w3.org\/2001\/XMLSchema\"\n\txmlns:sql = \"urn:schemas-microsoft-com:mapping-schema\"&gt;\n\t&#160;&lt;xsd:annotation&gt;\n\t&#160; &lt;xsd:appinfo&gt;\n\t&#160;&#160; &lt;sql:relationship name = \"InvoiceToLine\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; parent= \"Invoice\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; parent-key = \"ID\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; child= \"Invoice_Lines\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; child-key= \"InvoiceID\" \/&gt;\n\t&#160; &lt;\/xsd:appinfo&gt;\n\t&#160;&#160; &lt;\/xsd:annotation&gt;\n\t&#160;&#160;&#160; &lt;xsd:element name = \"Invoice\" sql:relation = \"Invoice\" &gt;\n\t&#160;&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ID\" type = \"xsd:integer\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;sql:field = \"ID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"InvoiceNumber\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"InvoiceNumber\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"DeliveryCharge\" type = \"xsd:decimal\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"DeliveryCharge\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"Invoice_Lines\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:relation = \"Invoice_Lines\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:relationship = \"InvoiceToLine\" &gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"ID\" type = \"xsd:integer\" \n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"ID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"InvoiceID\" type = \"xsd:integer\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"InvoiceID\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;xsd:element name = \"SalePrice\" type = \"xsd:string\"\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sql:field = \"SalePrice\" \/&gt;\n\t&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160;&#160;&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&#160;&#160;&#160; &lt;\/xsd:element&gt;\n\t&#160;&#160;&#160; &lt;\/xsd:sequence&gt;\n\t&#160;&#160; &lt;\/xsd:complexType&gt;\n\t&#160;&lt;\/xsd:element&gt;\n\t&lt;\/xsd:schema&gt;\n<\/pre>\n<p class=\"caption\">Listing 14: SQLXMLBulkLoadImportReferential.xsd<\/p>\n<p class=\"MsoNormal\">Modify the <code> objBL.Execute<\/code> statement in the existing SQLXMLBulkloadMulti.vbs file so that it refers to the new .XML and .XSD files, as shown in  Listing 13. Save this file as SQLXMLBulkloadNested.vbs.<\/p>\n<pre>\t...\n\tobjBL.Execute \"C:\\SQLXMLArticle\\SQLXMLBulkLoadImportReferential.xsd\" C:\\SQLXMLArticle\\SQLXMLSourceDataRelatedTables.xml\n\t...\n<\/pre>\n<p class=\"caption\">Listing 14: SQLXMLBulkloadNested.vbs (extract)<\/p>\n<p class=\"MsoNormal\">This example builds on the approach from the previous example. However, it has  the following prerequisites:<\/p>\n<ul>\n<li>Destination tables with  \t\t<code>PRIMARY KEY<\/code> to  \t\t<code>FOREIGN KEY<\/code> relationships. This is why I suggested that you re-create the destination  tables, as the full DDL makes the key relationships clearer.<\/li>\n<li>Source data where the &#8220;parent-child&#8221; data relationship is directly  expressed in the XML structure. This is the case in the source data where the  \t\t<code>&lt;Invoice&gt;<\/code> element contains one or more  \t\t<code>&lt;Invoice_Lines&gt;<\/code> elements.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">More generally, if the source XML is hierarchically structured to map to a  relational source, then SQLXML Bulk Load can shred the data into SQL Server tables while maintaining foreign key  relationships.<\/p>\n<h3>The Schema File for Nested XML Structures<\/h3>\n<p class=\"MsoNormal\">Here, as was the case in the previous example, both the key to success and any  potential complexity reside in the accurate definition of the XSD file. Indeed, everything that is essential to a  successful load is found in the schema file:<\/p>\n<ul>\n<li>Firstly, note the <code> \t\t&lt;xsd:annotation&gt;<\/code> and  \t\t<code>&lt;xsd:appinfo&gt;<\/code>  elements at the start of the schema file. The foreign key mapping is specified inside these elements. It must be present  &#8211; and correctly defined &#8211; for the load to work.<\/li>\n<li>Inside these elements, you have the  \t\t<code>sql:relationship name = \"InvoiceToLine\"<\/code>  element. This identifies each relationship between tables as part of the  \t\t<code>appinfo<\/code>, as it will then be  used as part of the <code>sql:element<\/code> definition for a  &#8220;table&#8221; in the schema file.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">The relationship definitions are as follows:<\/p>\n<ol>\n<li><code>  parent = \"Invoice\"<\/code>:   The &#8220;parent&#8221; table.<\/li>\n<li><code>  parent-key = \"ID\"<\/code>:   The primary key column of the &#8220;parent&#8221; table.<\/li>\n<li><code>  child = \"Invoice_Lines\"<\/code>:  The &#8220;child&#8221; table.<\/li>\n<li><code>  child-key = \" InvoiceID \"<\/code>:  The foreign key column of the &#8220;child&#8221; table.<\/li>\n<li><code>  sql:relationship = \"InvoiceToLine\"<\/code>: This attribute Inside the XML element, which  contains the child elements specifies the relationship to use that was defined previously in the schema file.<\/li>\n<\/ol>\n<p class=\"MsoNormal\">As you can see from this information, the ID field is now considered as a  <code> PRIMARY KEY<\/code> in the <code> Invoice<\/code> table, and the  <code>  InvoiceID<\/code> is taken to be the <code> FOREIGN KEY<\/code> in the <code> Invoice_Lines<\/code> table. Executing  the .vbs script loads the data into the two tables, while maintaining the ID as the <code> FOREIGN KEY<\/code> constraint for the  <code> Invoice_Lines<\/code>.<\/p>\n<p class=\"MsoNormal\">You also need to know that SQLXML Bulk Load generates records as their nodes  enter into scope, and sends those records to SQL Server as their nodes exit scope. This means that all the data for the  record <b>must<\/b> be present within the scope of the node. Consequently, the data  for the &#8220;child&#8221; table cannot precede the data for the &#8220;parent&#8221; table in the source file. To make the point in another  way, if you have a mapping schema that defines a <code> PRIMARY KEY<\/code> \/<code> FOREIGN KEY<\/code>  relationship between two tables (such as between <code> Invoice<\/code> and <code>  Invoice_Lines<\/code>), the table with the <code> PRIMARY KEY<\/code> must be described first in the schema. The  table with the <code> FOREIGN KEY<\/code> column must appear later in the schema.<\/p>\n<h3>Performance<\/h3>\n<p class=\"MsoNormal\">An important practical point about loading XML data using this particular design  pattern is speed, or rather lack of it. You will never attain the turbo-charged load times with nested XML that you can  attain with the &#8220;table dump&#8221; approach that you saw previously. This is not to say that you cannot load gigabytes of data  in reasonable timescales, just that you will never break any speed limits using this approach.<\/p>\n<p class=\"MsoNormal\">Equally, there are limits to the complexity of the XML source files that SQLXML  Bulk Loader can load gracefully (or at all, in some cases). It is well-nigh impossible to predict exactly when you will  hit a ceiling on the data ingestion capacities of this tool, but you do need to be aware that there are files that  appear to be too intricate for it to handle. <\/p>\n<h2>Conclusion<\/h2>\n<p class=\"MsoNormal\">Welcome to the world of SQLXML Bulk Loader! It&#8217;s a valuable tool that has been  available for many years and yet is still underused, in my opinion. You have seen how to load multiple types of XML data  files, from the simple table, via multiple separate tables through to more complex, nested XML structures. While this  tool will never win any prizes for making developer&#8217;s lives easier, it certainly makes up for this in its raw power and  speed. If you suspect that your XML files will ever grow to any appreciable size, then you could well be advised to get  to know this versatile addition to your ETL toolkit sooner rather than later. It just could save your reputation.<\/p>\n<p class=\"MsoNormal\">Look out for the next installment, covering some of the more advanced features  of SQLXML Bulk Loader!<\/p>\n<div class=\"STTipsHeader note\">\n<p class=\"note\"> <a href=\"http:\/\/www.amazon.com\/Business-Intelligence-Server-Reporting-Services\/dp\/1484205332\/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1428744137&amp;sr=1-1&amp;keywords=business+intelligence+with+sql+server+reporting+services\"> SQL Server 2012 Data Integration  Recipes<\/a><br \/>If you like what you read in this article and want to learn more about data  integration using SQL Server, then please take a look at my book, <a href=\"http:\/\/www.amazon.com\/Server-2012-Data-Integration-Recipes\/dp\/1430247916\/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1430855181&amp;sr=1-1&amp;keywords=sql+server+2012+data+integration+recipes\"> SQL Server 2012 Data Integration Recipes<\/a> (Apress, December 2012)<\/p>\n<\/p><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>SQLXML isn&#8217;t exactly new technology, but like the even more venerable BCP, it remains the quickest and most reliable way of heaving large quantities of data into SQL Server databases. SQLXML is very versatile, and once set up is wonderfully reliable ETL system, but isn&#8217;t trivial to learn. Adam Aspin comes to the rescue with a simple guide.&hellip;<\/p>\n","protected":false},"author":2181,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143532],"tags":[4156,4242,4168,4824,4150,4151,4213,4217],"coauthors":[],"class_list":["post-2009","post","type-post","status-publish","format-standard","hentry","category-tools-sql-server","tag-asp","tag-basics","tag-database","tag-etl","tag-sql","tag-sql-server","tag-sql-tools","tag-xml"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2009","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/2181"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2009"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2009\/revisions"}],"predecessor-version":[{"id":54530,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2009\/revisions\/54530"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2009"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2009"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2009"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2009"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}