I'm using SQL Server 2012 to import a simple XML document. Here's a sample of what the XML looks like:
<Orders>
<Order>
<Customer>Bob Smith</Customer>
<Address>123 Main St, Anytown, NY</Address>
<OrderItems>
<Item>
<ItemName>Table</ItemName>
<Quantity>1</Quantity>
</Item>
<Item>
<ItemName>Chair</ItemName>
<Quantity>4</Quantity>
</Item>
</OrderItems>
</Order>
<Order>
<Customer>Jane Doe</Customer>
<Address>456 Broadway Ave, Someplace, TX</Address>
<OrderItems>
<Item>
<ItemName>Banana Slicer</ItemName>
<Quantity>1</Quantity>
</Item>
</OrderItems>
</Order>
<Order>
<Customer>Joe Public</Customer>
<Address>789 Euclid Rd, Random, ID</Address>
<OrderItems>
<Item>
<ItemName>Hammer</ItemName>
<Quantity>1</Quantity>
</Item>
<Item>
<ItemName>Nails</ItemName>
<Quantity>50</Quantity>
</Item>
<Item>
<ItemName>Chisel</ItemName>
<Quantity>2</Quantity>
</Item>
</OrderItems>
</Order>
</Orders>
Note that each order can have one or more items in it.
Here are the destination tables (using temp tables for now):
CREATE TABLE dbo.#Order
(
[OrderID] int IDENTITY(1001,1) PRIMARY KEY,
[Customer] varchar(200) NOT NULL,
[Address] varchar(200) NOT null
);
CREATE TABLE dbo.#OrderItem
(
[OrderItemID] int IDENTITY(5001,1) PRIMARY KEY,
[OrderID] int
FOREIGN KEY REFERENCES dbo.#Order(OrderID)
ON DELETE CASCADE,
[ItemName] varchar(100) NOT NULL,
[Quantity] int NOT NULL
);
The above should be imported as follows:
OrderID Customer Address
------- -------- -------
1001 Bob Smith 123 Main St, Anytown, NY
1002 Jane Doe 456 Broadway Ave, Someplace, TX
1003 Joe Public 789 Euclid Rd, Random, ID
OrderItemID OrderID ItemName Quantity
----------- ------- -------- --------
5001 1001 Table 1
5002 1001 Chair 4
5003 1002 Banana Slicer 1
5004 1003 Hammer 1
5005 1003 Nails 50
5006 1003 Chisel 2
Is there a way to insert all the data into both the Order
and the OrderItem
tables without using a cursor? I'm not against using a cursor for this, but I would like to know if there is a simpler, set-based alternative. The tricky part is that OrderItem
needs to know about the inserted OrderID
of each order that was imported.
Here's my initial attempt at importing the data (using a cursor). I'm using temp tables (instead of permnanent tables) and hardcoding the XML to make it easier to just copy and run this code:
-------------------------------------------------------------------------------
-- Create Temp Tables
-------------------------------------------------------------------------------
IF OBJECT_ID('tempdb.dbo.#OrderItem') IS NOT NULL
DROP TABLE #OrderItem
GO
IF OBJECT_ID('tempdb.dbo.#Order') IS NOT NULL
DROP TABLE #Order
GO
CREATE TABLE dbo.#Order
(
[OrderID] int IDENTITY(1001,1) PRIMARY KEY,
[Customer] varchar(200) NOT NULL,
[Address] varchar(200) NOT null
);
CREATE TABLE dbo.#OrderItem
(
[OrderItemID] int IDENTITY(5001,1) PRIMARY KEY,
[OrderID] int
FOREIGN KEY REFERENCES dbo.#Order(OrderID)
ON DELETE CASCADE,
[ItemName] varchar(100) NOT NULL,
[Quantity] int NOT NULL
);
-------------------------------------------------------------------------------
-- Define Sample XML Document
-------------------------------------------------------------------------------
DECLARE @xml xml = '
<Orders>
<Order>
<Customer>Bob Smith</Customer>
<Address>123 Main St, Anytown, NY</Address>
<OrderItems>
<Item>
<ItemName>Table</ItemName>
<Quantity>1</Quantity>
</Item>
<Item>
<ItemName>Chair</ItemName>
<Quantity>4</Quantity>
</Item>
</OrderItems>
</Order>
<Order>
<Customer>Jane Doe</Customer>
<Address>456 Broadway Ave, Someplace, TX</Address>
<OrderItems>
<Item>
<ItemName>Banana Slicer</ItemName>
<Quantity>1</Quantity>
</Item>
</OrderItems>
</Order>
<Order>
<Customer>Joe Public</Customer>
<Address>789 Euclid Rd, Random, ID</Address>
<OrderItems>
<Item>
<ItemName>Hammer</ItemName>
<Quantity>1</Quantity>
</Item>
<Item>
<ItemName>Nails</ItemName>
<Quantity>50</Quantity>
</Item>
<Item>
<ItemName>Chisel</ItemName>
<Quantity>2</Quantity>
</Item>
</OrderItems>
</Order>
</Orders>';
-------------------------------------------------------------------------------
-- Query XML Document
-------------------------------------------------------------------------------
--SELECT
-- Tbl.Col.value('Customer[1]', 'varchar(200)') AS [Customer],
-- Tbl.Col.value('Address[1]', 'varchar(200)') AS [Address],
-- Tbl.Col.query('./OrderItems/Item') AS [ItemsXML]
--FROM
-- @xml.nodes('//Order') AS Tbl(Col)
-------------------------------------------------------------------------------
-- Import XML document (Attempt 1 - using a cursor)
-------------------------------------------------------------------------------
DECLARE @OrderNode xml;
DECLARE @OrderID int;
DECLARE cur CURSOR FAST_FORWARD READ_ONLY LOCAL FOR
SELECT Tbl.Col.query('.') FROM @xml.nodes('//Order') AS Tbl(Col)
OPEN cur
FETCH NEXT FROM cur INTO @OrderNode
WHILE @@FETCH_STATUS = 0 BEGIN
------------------------------------------
-- Import order
------------------------------------------
INSERT INTO #Order
(
[Customer],
[Address]
)
SELECT
Tbl.Col.value('Customer[1]', 'varchar(200)'),
Tbl.Col.value('Address[1]', 'varchar(200)')
FROM @OrderNode.nodes('Order') AS Tbl(Col);
------------------------------------------
-- Get the inserted order ID
------------------------------------------
SELECT @OrderID = SCOPE_IDENTITY();
------------------------------------------
-- Import order items
------------------------------------------
INSERT INTO #OrderItem
(
OrderID,
ItemName,
Quantity
)
SELECT
@OrderID,
Tbl.Col.value('ItemName[1]', 'varchar(100)'),
Tbl.Col.value('Quantity[1]', 'int')
FROM @OrderNode.nodes('Order/OrderItems/Item') AS Tbl(Col);
-------------------------------------------------------------------------------
-- Move on to next order
-------------------------------------------------------------------------------
FETCH NEXT FROM cur INTO @OrderNode
END
CLOSE cur
DEALLOCATE cur
-------------------------------------------------------------------------------
-- Show results
-------------------------------------------------------------------------------
SELECT * FROM #Order
SELECT * FROM #OrderItem