我正在使用SQL Server 2012导入一个简单的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>
请注意,每个订单中都可以包含一个或多个商品。
以下是目标表(暂时使用临时表):
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
);
上述内容应按如下方式导入:
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
有没有办法在不使用游标的情况下将所有数据插入Order
和OrderItem
表中?我不反对使用光标,但我想知道是否有一个更简单的,基于集合的替代方案。棘手的部分是OrderItem
需要知道导入的每个订单的插入OrderID
。
这是我最初尝试导入数据(使用游标)。我正在使用临时表(而不是permnanent表)并对XML进行硬编码,以便更容易复制和运行此代码:
-------------------------------------------------------------------------------
-- 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
答案 0 :(得分:1)
要扩展xQbert的注释,如果您的示例XML数据在XML变量@x中,您可以使用以下命令来放置所有订单&amp;将商品订购到临时表中:
DECLARE @x XML = '... your sample xml ...';
SELECT Ord.n.value('for $i in . return count(../*[. << $i]) + 1', 'int') AS OrderNbr
, Ord.n.value('./Customer[1]','varchar(200)') AS Customer
, Ord.n.value('./Address[1]','varchar(200)') AS Address
, Item.n.value('./ItemName[1]','varchar(200)') AS ItemName
, Item.n.value('./Quantity[1]','int') AS Quantity
INTO #Orders
FROM @x.nodes('/Orders/Order') AS Ord(n)
CROSS APPLY Ord.n.nodes('./OrderItems/Item') AS Item(n);
然后你需要做两次插入,一次插入包含DISTINCT主顺序记录的父表,用OUTPUT子句保留标识值,第二次插入包括所有订单项并使用第一次保存的PK值插入。看到这个链接:
How can I INSERT data into two tables simultaneously in SQL Server?
答案 1 :(得分:1)
进一步构建Kevin Suchlicki的答案,如果您将XML声明为XML变量@x,然后插入以下内容,您将在原始帖子中获得结果:
DECLARE @Order TABLE ([OrderID] int IDENTITY(1001,1) PRIMARY KEY,
[Customer] varchar(200) NOT NULL,
[Address] varchar(200) NOT NULL)
DECLARE @OrderItem TABLE (
[OrderItemID] int IDENTITY(5001,1) PRIMARY KEY,
[OrderID] int,
[ItemName] varchar(100) NOT NULL,
[Quantity] int NOT NULL
)
INSERT INTO @Order (Customer, Address)
SELECT t.c.value('(Customer)[1]','varchar(200)') AS 'Customer',
t.c.value('(Address)[1]','varchar(200)') AS 'Address'
FROM @x.nodes('Orders/Order') t(c);
INSERT INTO @OrderItem (OrderID, ItemName, Quantity)
SELECT
Ordr.OrderID
, Item.n.value('./ItemName[1]','varchar(200)') AS ItemName
, Item.n.value('./Quantity[1]','int') AS Quantity
FROM @x.nodes('Orders/Order') Ord(n)
CROSS APPLY Ord.n.nodes('./OrderItems/Item') AS Item(n)
JOIN @Order Ordr ON Ordr.Customer = Ord.n.value('./Customer[1]','varchar(200)')
SELECT *
FROM @Order
SELECT *
FROM @OrderItem
显然这里使用表变量,但你可以很容易地将它们换成临时表。