sql:relationship and the Key Ordering Rule (SQLXML 4.0)

Because XML Bulk Load generates records as their nodes enter into scope and sends those records to Microsoft SQL Server as their nodes exit scope, the data for the record must be present within the scope of the node. 

Consider the following XSD schema, in which the one-to-many relationship between <Customer> and <Order> elements (one customer can place many orders) is specified by using the <sql:relationship> element:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"<> 
            xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
  <xsd:appinfo>
    <sql:relationship name="CustCustOrder"
          parent="Cust"
          parent-key="CustomerID"
          child="CustOrder"
          child-key="CustomerID" />
  </xsd:appinfo>
</xsd:annotation>

  <xsd:element name="Customers" sql:relation="Cust" >
   <xsd:complexType>
     <xsd:sequence>
       <xsd:element name="CustomerID"  type="xsd:integer" />
       <xsd:element name="CompanyName" type="xsd:string" />
       <xsd:element name="City"        type="xsd:string" />
       <xsd:element name="Order" 
                          sql:relation="CustOrder"
                          sql:relationship="CustCustOrder" >
         <xsd:complexType>
          <xsd:attribute name="OrderID" type="xsd:integer" />
         </xsd:complexType>
       </xsd:element>
     </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

As the <Customer> element node enters into scope, XML Bulk Load generates a customer record. This record stays until XML Bulk Load reads </Customer>. In processing the <Order> element node, XML Bulk Load uses <sql:relationship> to obtain the value of the CustomerID foreign key column of the CustOrder table from the <Customer> parent element, because the <Order> element does not specify the CustomerID attribute. This means that in defining the <Customer> element, you must specify the CustomerID attribute in the schema before you specify <sql:relationship>. Otherwise, when an <Order> element enters into scope, XML Bulk Load generates a record for the CustOrder table, and when the XML Bulk Load reaches the </Order> end tag, it sends the record to SQL Server without the CustomerID foreign key column value.

To test a working sample

Save the schema that is provided in this example as SampleSchema.xml.

  1. Create these tables:

    CREATE TABLE Cust (
                  CustomerID     int          PRIMARY KEY,
               CompanyName    varchar(20)  NOT NULL,
                  City           varchar(20)  DEFAULT 'Seattle')
    GO
    CREATE TABLE CustOrder (
                  OrderID        varchar(10) PRIMARY KEY,
               CustomerID     int         FOREIGN KEY REFERENCES                                          Cust(CustomerID))
    GO
    
  2. Save the following sample data as SampleXMLData.xml:

    <ROOT>  
      <Customers>
        <CompanyName>Hanari Carnes</CompanyName>
        <City>NY</City>
        <Order OrderID="1" />
        <Order OrderID="2" />
        <CustomerID>1111</CustomerID>
      </Customers>
      <Customers>
        <CompanyName>Toms Spezialitten</CompanyName>
         <City>LA</City>  
        <Order OrderID="3" />
        <CustomerID>1112</CustomerID>
      </Customers>
      <Customers>
        <CompanyName>Victuailles en stock</CompanyName>
        <Order OrderID="4" />
        <CustomerID>1113</CustomerID>
      </Customers>
    </ROOT>
    
  3. To execute XML Bulk Load, save and execute the following Microsoft Visual Basic Scripting Edition (VBScript) example as MySample.vbs:

    set objBL = CreateObject("SQLXMLBulkLoad.SQLXMLBulkload.4.0")
    objBL.ConnectionString = "provider=SQLOLEDB;data source=localhost;database=tempdb;integrated security=SSPI"
    objBL.ErrorLogFile = "c:\error.log"
    objBL.CheckConstraints = True
    objBL.Transaction=True
    objBL.Execute "c:\SampleSchema.xml", "c:\SampleXMLData.xml"
    set objBL=Nothing
    

    The result is that XML Bulk Load inserts a NULL value in the CustomerID foreign key column of the CustOrder table. If you revise the XML sample data so that the <CustomerID> child element appears before the <Order> child element, you get the expected result: XML Bulk Load inserts the specified foreign key value into the column.

This is the equivalent XDR schema:

<?xml version="1.0" ?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data" 
        xmlns:dt="urn:schemas-microsoft-com:xml:datatypes"  
        xmlns:sql="urn:schemas-microsoft-com:xml-sql" > 
   <ElementType name="CustomerID"  />
   <ElementType name="CompanyName" />
   <ElementType name="City"        />

   <ElementType name="root" sql:is-constant="1">
      <element type="Customers" />
   </ElementType>

   <ElementType name="Customers" sql:relation="Cust" >
      <element type="CustomerID" sql:field="CustomerID" />
      <element type="CompanyName" sql:field="CompanyName" />
      <element type="City" sql:field="City" />
      <element type="Order" >
                 <sql:relationship
                        key-relation    ="Cust"
                        key             ="CustomerID"
                        foreign-key     ="CustomerID"
                        foreign-relation="CustOrder" />
      </element>
   </ElementType>
    <ElementType name="Order" sql:relation="CustOrder" >
      <AttributeType name="OrderID" />
      <AttributeType name="CustomerID" />
      <attribute type="OrderID" />
      <attribute type="CustomerID" />
    </ElementType>
</Schema>