在SQL Server 2008中使用OPENXML存储过程 - INSERT命令与XML文档不同

时间:2012-04-03 16:30:09

标签: sql xml sql-server-2008 sqlxml

我正在使用SQL Server 2008的XML解析功能来遍历XML文档并对每个元素执行INSERT。

但是,我的存储过程似乎是按照与文档中的顺序不同的顺序将每个元素插入到表中。

此外,我尝试这次的次数越多,INSERT顺序似乎就会改变。

以下是XML文档的示例 - 这里没什么好看的。

<ts>
    <t id="36a3c8c1-b958-42f0-82d1-dfa6bf9b99a1" encryptedAccountId="fQ/XF8lpeR9wEDUV3yMzvQ==" uploaded="2012-04-03T15:49:19.9615097Z" visible="1">
        <tv fieldId="301" officialValue="0, 0" friendlyValue="0, 0" />
        <tv fieldId="302" officialValue="0, 1" friendlyValue="0, 1" />
        <tv fieldId="303" officialValue="0, 2" friendlyValue="0, 2" />
        <tv fieldId="304" officialValue="0, 3" friendlyValue="0, 3" />
        <tv fieldId="305" officialValue="0, 4" friendlyValue="0, 4" />
        <tv fieldId="306" officialValue="0, 5" friendlyValue="0, 5" />
    </t>
    <t id="9d56d082-4b6a-4bdf-a7a2-f5c6af88344e" encryptedAccountId="fQ/XF8lpeR9wEDUV3yMzvQ==" uploaded="2012-04-03T15:49:19.9615097Z"  visible="1">
        <tv fieldId="301" officialValue="1, 0" friendlyValue="1, 0" />
        <tv fieldId="302" officialValue="1, 1" friendlyValue="1, 1" />
        <tv fieldId="303" officialValue="1, 2" friendlyValue="1, 2" />
        <tv fieldId="304" officialValue="1, 3" friendlyValue="1, 3" />
        <tv fieldId="305" officialValue="1, 4" friendlyValue="1, 4" />
        <tv fieldId="306" officialValue="1, 5" friendlyValue="1, 5" />
    </t>
    <t id="27db47a3-ad3f-4279-8f4f-0a8944ce32d4" encryptedAccountId="fQ/XF8lpeR9wEDUV3yMzvQ==" uploaded="2012-04-03T15:49:19.9615097Z" visible="1">
        <tv fieldId="301" officialValue="2, 0" friendlyValue="2, 0" />
        <tv fieldId="302" officialValue="2, 1" friendlyValue="2, 1" />
        <tv fieldId="303" officialValue="2, 2" friendlyValue="2, 2" />
        <tv fieldId="304" officialValue="2, 3" friendlyValue="2, 3" />
        <tv fieldId="305" officialValue="2, 4" friendlyValue="2, 4" />
        <tv fieldId="306" officialValue="2, 5" friendlyValue="2, 5" />
    </t>
    <t id="867ea26b-0341-4d60-ac48-f305492a60f0" encryptedAccountId="fQ/XF8lpeR9wEDUV3yMzvQ==" uploaded="2012-04-03T15:49:19.9615097Z" visible="1">
        <tv fieldId="301" officialValue="3, 0" friendlyValue="3, 0" />
        <tv fieldId="302" officialValue="3, 1" friendlyValue="3, 1" />
        <tv fieldId="303" officialValue="3, 2" friendlyValue="3, 2" />
        <tv fieldId="304" officialValue="3, 3" friendlyValue="3, 3" />
        <tv fieldId="305" officialValue="3, 4" friendlyValue="3, 4" />
        <tv fieldId="306" officialValue="3, 5" friendlyValue="3, 5" />
    </t>
</ts>

存储过程发生了一些操作,但我已经注释掉其他部分,只留下插入<t/>元素然后插入<tv/>元素的SQL。

存储过程中的SQL如下所示。

@xmlTransaction是包含上述XML的NVARCHAR (MAX)输入参数

BEGIN
    SET NOCOUNT ON;

    DECLARE @encryptedAccountID AS VARCHAR(200)

    BEGIN TRANSACTION
        BEGIN TRY
            DECLARE @Handle AS INT
            DECLARE @TransactionCount AS INT

            EXEC sp_xml_preparedocument @Handle OUTPUT, @xmlTransaction

            /* encryptedAccountId is always the same for each @xmlTransaction param */
            /* Just take the value from the first <t/> element */
            SET @encryptedAccountID = (SELECT eID FROM OPENXML (@Handle, '/ts/t[1]', 2) WITH ( eID VARCHAR '@encryptedAccountId' ))

            /* Go through each <t/> element in the XML document and INSERT */
            INSERT INTO
            [Transactions] 
            (
                [ID],
                [EncryptedAccountID]
            )
            SELECT
                *
            FROM
                OPENXML (@Handle, '/ts/t', 2)
            WITH
            (
                rID UNIQUEIDENTIFIER '@id',
                rEncryptedAccountID VARCHAR (200) '@encryptedAccountId'
            )

            /* Loop through each TransactionValue in the XML document and INSERT */
            INSERT INTO
            [TransactionValues]
            (
                FieldID,
                TransactionID,
                OfficialValue,
                FriendlyValue
            )
            SELECT
                *
            FROM
                OPENXML (@Handle, '/ts/t/tv', 2)
            WITH
            (
                rFieldID INT '@fieldId',
                rTransactionID UNIQUEIDENTIFIER '../@id',
                rOfficialValue NVARCHAR (500) '@officialValue',
                rFriendlyValue NVARCHAR (500) '@friendlyValue'
            )

            /* Dispose of the XML document */
            EXEC sp_xml_removedocument @Handle

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH

        RETURN @@ERROR

        ROLLBACK TRANSACTION        
    END CATCH

END

应该相当简单。然而,如果我查询结果,它们与XML文档的顺序不同。 <tv/>元素的第二个INSERT语句以正确的顺序将元素存储到第二个表中,但是<t/>个元素存储在他们的表中以正确的顺序。

有人可以向我解释为什么<t/>元素没有按照它们在XML文档中出现的顺序插入到表中吗?

2 个答案:

答案 0 :(得分:3)

如果我在SQL Server中使用本机XQuery支持而不是“遗留”OPENXML内容,那么<t>节点确实会按照它们在XML文档中出现的顺序插入到表中。

我使用的代码如下:

INSERT INTO dbo.[Transactions]([ID], [EncryptedAccountID])
   SELECT
        XT.value('@id', 'uniqueidentifier'),
        XT.value('@encryptedAccountId', 'varchar(200)')
   FROM
      @xmlTransaction.nodes('/ts/t') AS Nodes(XT)

同样可以为<tv>子节点做同样的事情。

答案 1 :(得分:1)

据我所知,OPENXML上的文档并不保证订单的任何内容。并且在关系表中也不保证顺序。那么为什么不只是“按订单”某一列来获得你想要的订单呢?这总是如何在sql中强制执行排序。

我不明白为什么你要单独提取encryptedAccountId属性。为什么不直接将它插入maininsert语句?

不相关的提示,如果您的事务插入生成一个标识,您可以通过加入'../@id'上的父表来获取它的值的副本。即使您不需要父表中的任何其他内容,也可以确保没有任何事务插入失败。

提示示例:

 INSERT INTO
            [TransactionValues]
            (
                FieldID,
                TransactionID,
                OfficialValue,
                FriendlyValue,
                ParentIdentityFK --new
            )
            SELECT
                shredded.rFieldID, shredded.rTransactionID, shredded.rOfficialValue, shredded.rFriendlyValue, t.SomeIdentity
            FROM
                OPENXML (@Handle, '/ts/t/tv', 2)                
            WITH
            (
                rFieldID INT '@fieldId',
                rTransactionID UNIQUEIDENTIFIER '../@id',
                rOfficialValue NVARCHAR (500) '@officialValue',
                rFriendlyValue NVARCHAR (500) '@friendlyValue'
            ) as shredded
            join Transactions t
                on shredded.rTransactionID = t.ID