Inserting Parent and Child Records
Use Case
Sometimes, when inserting records, it's necessary to supply details about child records that have a dependency on a parent.
For example, when dealing with a CRM system, Invoices often cannot be entered without at least one line item. Since invoice line items can have several fields, this presents a unique challenge when offering the data as relational tables. When reading the data, it is easy enough to model an Invoice and an InvoiceLineItem table with a foreign key connecting the two. However, during inserts, the CRM system requires both the Invoice and the InvoiceLineItems to be created in a single submission.
To solve this sort of problem, our tools offer child collection columns on the parent. These columns can be used to submit insert statements that include details of both the parent and the child records.
For example, let's say that the Invoice table contains a single column called InvoiceLineItems. During the insert, we can pass the details of the records that must be inserted to the InvoiceLineItems table into Invoice record's InvoiceLineItems column.
The following subsection describes how this might be done.
Methods for Inserting Parent/Child Records
The connector facilitates two methods for inserting parent/child records: temporary table insertion and XML aggregate insertion.Temporary (#TEMP) tables
The simplest way to enter data would be to use a #TEMP table, or temporary table, which the connector will store in memory.
Reference the #TEMP table with the following syntax:
TableName#TEMP
#TEMP tables are stored in memory for the duration of a connection.
Therefore, in order to use them, you cannot close the connection between submitting inserts to them, and they cannot be used in environments where a different connection may be used for each query.
Within that single connection, the table remains in memory until the bulk insert is successful, at which point the temporary table will be wiped from memory.
For example:
INSERT INTO InvoiceLineItems#TEMP (ReferenceNumber, Item, Quantity, Amount) VALUES ('INV001', 'Basketball', 10, 9.99) INSERT INTO InvoiceLineItems#TEMP (ReferenceNumber, Item, Quantity, Amount) VALUES ('INV001', 'Football', 5, 12.99)
Once the InvoiceLineItems table is populated, the #TEMP table may be referenced during an insert into the Invoice table:
INSERT INTO Invoices (ReferenceNumber, Customer, InvoiceLines) VALUES ('INV001', 'John Doe', 'InvoiceLineItems#TEMP')
Under the hood, the connector will read in values from the #TEMP table.
Notice that the ReferenceNumber was used to identify what Invoice the lines are tied to. This is because the
#TEMP table may be populated and used with a bulk insert, where there are separate lines for each invoice. This enables the #TEMP tables to be used with a bulk insert. For example:
INSERT INTO Invoices#TEMP (ReferenceNumber, Customer, InvoiceLines) VALUES ('INV001', 'John Doe', 'InvoiceLineItems#TEMP') INSERT INTO Invoices#TEMP (ReferenceNumber, Customer, InvoiceLines) VALUES ('INV002', 'Jane Doe', 'InvoiceLineItems#TEMP') INSERT INTO Invoices SELECT ReferenceNumber, Customer, InvoiceLines FROM Invoices#TEMP
In this case, we are inserting two different Invoices. The ReferenceNumber is how we determine which Lines go with which Invoice.
Note: The tables and columns presented here are an example of how the connector works in general. The specific table and column names may be different in the connector.
Direct XML Insertion
Direct XML can be used as an alternative to #TEMP tables. Since #TEMP tables are not used to construct them, it does not matter if you use the same connection or close the connection after insert.
For example:
[ { "Item", "Basketball", "Quantity": 10 "Amount": 9.99 }, { "Item", "Football", "Quantity": 5 "Amount": 12.99 } ]
OR
<Row> <Item>Basketball</Item> <Quantity>10</Quantity> <Amount>9.99</Amount> </Row> <Row> <Item>Football</Item> <Quantity>5</Quantity> <Amount>12.99</Amount> </Row>
Note that the ReferenceNumber is not present in these examples because the XML, by its nature, is passed against the parent record in full per insert. Since the complete XML must be constructed and submitted for each row, there is no need to provide something to tie the child back to the parent.
Now insert the values:
INSERT INTO Invoices (ReferenceNumber, Customer, InvoiceLines) VALUES ('INV001', 'John Doe', '{...}')
OR
INSERT INTO Invoices (ReferenceNumber, Customer, InvoiceLines) VALUES ('INV001', 'John Doe', '<Row>...</Row>')
Note: The connector also supports the use of XML/JSON aggregates.
Example for Azure DevOps
For a working example of how temp tables can be used to insert data in Azure DevOps, please see the following. In Azure DevOps,
Note: the key references such as Id may be different in your environment:
// Execute bulk insert INSERT INTO TestResults#TEMP (Projectid, Testrunid, TestCaseTitle, AutomatedTestName, Priority, Outcome) VALUES ('b154d8f3-bfd9-4bfb-90ae-2e6c8cda8937',1, 'VerifyWebsiteTheme', 'FabrikamFiber.WebSite.TestClass.VerifyWebsiteTheme', 1, 'Passed'); INSERT INTO TestResults#TEMP (Projectid, Testrunid, TestCaseTitle, AutomatedTestName, Priority, Outcome) VALUES ('b154d8f3-bfd9-4bfb-90ae-2e6c8cda8937',1, 'VerifyWebsiteLinks', 'FabrikamFiber.WebSite.TestClass.VerifyWebsiteLinks', 2, 'Failed'); INSERT INTO TestResults (Projectid, Testrunid, TestCaseTitle, AutomatedTestName, Priority, Outcome) SELECT Projectid, Testrunid, TestCaseTitle, AutomatedTestName, Priority, Outcome FROM testresults#TEMP // Execute bulk update INSERT INTO TestResults#TEMP (Projectid, Testrunid, Id, Comment, State) VALUES ('b154d8f3-bfd9-4bfb-90ae-2e6c8cda8937',1, 10000, 'Website theme is looking good', 'Completed'); INSERT INTO TestResults#TEMP (Projectid, Testrunid, Id, Comment, State) VALUES ('b154d8f3-bfd9-4bfb-90ae-2e6c8cda8937',1, 100001, 'Website links are failing because of incorrect container id', 'Completed'); Update TestResults (Projectid, Testrunid, Id, Comment, State) SELECT Projectid, Testrunid, Id, Comment, State FROM testresults#TEMP