通过OPENXML插入
现在我们将用下面的XML示例从中插入一个定单和两个定单细节行:
首先,将该XML传递给图 7所示的存储过程,并使用sp_xml_preparedocument系统存储过程准备之。然后启动一个事务来包装INSERT语句,以便插入一个定单及其子记录。这样便可以在部分事务失败时回滚事务。接下来,使用在Customer\Order节点开始的OPENXML打开XML文档。利用WITH子句,将XML文档返回到Order元素(Customer 元素)的父节点,然后查看Customer元素的CustomerID属性值,从而获得CustomerID。这是OPENXML函数的一个重要功能,因为它让用户使用受限的XPath表达式来遍历XML文档,从而获得属性和元素值。<Customer CustomerID='ALFKI'>
<Order OrderDate='1/1/1972' Freight='3'>
<Detail ProductID='1' Price='4' Quantity='10' Discount='0'/>
<Detail ProductID='2' Price='5' Quantity='2' Discount='0'/>
</Order>
</Customer>
图 7 插入父定单及其子定单
CREATE PROCEDURE prInsertOrderAndOrderDetailsFromXML @sXML NTEXT
AS
DECLARE @iDoc int, @OrderID int
![]()
EXEC sp_xml_preparedocument @iDoc output, @sXML
IF @@Error<>0
BEGIN
RETURN
END
![]()
BEGIN TRANSACTION
INSERT INTO Orders (CustomerID , OrderDate, Freight)
SELECT CustomerID, CAST(OrderDate As datetime), CAST(Freight AS money)
FROM OpenXML(@iDoc,'/Customer/Order', 1)
WITH (CustomerID nchar(5) '../@CustomerID' ,
OrderDate varchar(10) '@OrderDate',
Freight varchar(12) '@Freight')
![]()
IF @@Error<>0
BEGIN
EXEC sp_xml_removedocument @iDoc
ROLLBACK TRANSACTION
RETURN
END
![]()
SET @OrderID=SCOPE_IDENTITY()
![]()
INSERT INTO [Order Details]
(OrderID, ProductID, UnitPrice, Quantity, Discount)
SELECT @OrderID, ProductID, ProductPrice, ProductQTY, Discount
FROM OpenXML(@iDoc,'/Customer/Order/Detail')
WITH (ProductID int '@ProductID', ProductPrice money '@Price',
ProductQTY smallint '@Quantity', Discount real '@Discount')
![]()
IF @@Error<>0
BEGIN
EXEC sp_xml_removedocument @iDoc
ROLLBACK TRANSACTION
RETURN
END
![]()
EXEC sp_xml_removedocument @iDoc
COMMIT TRANSACTION
SELECT @OrderID
GO
插入Order后,我们获取由内置的SQL Server SCOPE_IDENTITY函数刚刚生成的OrderID值。然后我们使用另一个INSERT语句(使用OPENXML函数从XML数据中获取Order Details)继续插入Order Details行。只要不出现错误,定单及其子定单细节行就被插入到其各自的数据库表中。
SCOPE_IDENTITY方法使用单个Order及其子定单。不过,以一个XML批插入多个Order及其子定单的情况比较复杂。问题就是在具有多个Order记录的情况下,仍要能够将适当的定单映射到其子定单。由于不知道要关联哪些行,因此必须添加一些代码来处理这一问题。可以在WITH子句中使用@mp:id/@mp:parentid元属性来提供一种获取父定单的新OrderID并将其映射到其子定单的OrderID字段的方法。
