SQL Server-在XML.value()中使用隐式SQL变量时出错

时间:2018-08-15 08:06:06

标签: sql-server xml tsql xpath xquery

以下代码有效,并且输出名字:

DECLARE @xmlData XML = '<CustomerExtract><Delete User="Customer Deleter" ReasonCode="2"><Customer><CustomerID>9</CustomerID><Name><Title></Title><First>MOCHA</First><MI></MI><Last>MOCHA</Last><Suffix></Suffix></Name></Customer></Delete></CustomerExtract>'
DECLARE @xmlPath VARCHAR(MAX) = '(/CustomerExtract/Delete/Customer/'
select @xmlData.value('(/CustomerExtract/Delete/Customer/Name/First)[1]','VARCHAR(8000)')

现在,我需要使用动态变量,以便可以根据情况更改XML路径,但外部元素不会更改。代码是这样的:

-- Check the CustomerUpdate type (Create / Update / Delete)
IF (@dataXML.exist('(/CustomerExtract/New)') = 1)
    SET @xmlPath = '/CustomerExtract/New/Customer/'
ELSE IF (@dataXML.exist('(/CustomerExtract/Change)') = 1)
    SET @xmlPath = '/CustomerExtract/Change/After/Customer/'
ELSE IF (@dataXML.exist('(/CustomerExtract/Delete)') = 1)
    SET @xmlPath = '/CustomerExtract/Delete/Customer/'
ELSE --Not supported!
    RETURN

但是,当我尝试使用此处提到的SQL隐式变量:The argument 1 of the XML data type method "value" must be a string literal时,出现了一些奇怪的错误:

示例1:在变量@xmlPath内的括号内

DECLARE @xmlPath VARCHAR(MAX) = '(/CustomerExtract/Delete/Customer/'
select @xmlData.value('sql:variable("@xmlPath")Name/First)[1]','VARCHAR(8000)')

它引发以下异常:

XQuery [value()]: No more tokens expected at the end of the XQuery expression. Found 'Name'.

示例2:不在变量上加括号

DECLARE @xmlPath VARCHAR(MAX) = '/CustomerExtract/Delete/Customer/'
select @xmlData.value('(sql:variable("@xmlPath")Name/First)[1]','VARCHAR(8000)')

它抛出此异常:

XQuery [value()]: ")" was expected.

示例3:

DECLARE @xmlPath VARCHAR(MAX) = '/CustomerExtract/Delete/Customer'
select @xmlData.value('(sql:variable("@xmlPath")/Name/First)[1]','VARCHAR(8000)')

它抛出此异常:

XQuery [value()]: A node or set of nodes is required for /

有人可以解释一下为什么会发生这种情况吗?什么是实现我想要的结果的正确语法?谢谢

1 个答案:

答案 0 :(得分:1)

XPath中的.value()必须是文字。您不能在此处注入可变部分,但是有几种选择:

DECLARE @xmlData XML = '<CustomerExtract><Delete User="Customer Deleter" ReasonCode="2"><Customer><CustomerID>9</CustomerID><Name><Title></Title><First>MOCHA</First><MI></MI><Last>MOCHA</Last><Suffix></Suffix></Name></Customer></Delete></CustomerExtract>'
DECLARE @xmlPath VARCHAR(MAX) = '(/CustomerExtract/Delete/Customer/'

-您可以动态声明整个内容...

DECLARE @cmd NVARCHAR(MAX)=
N'select @xmlData.value(''' +  @xmlPath + '/Name/First)[1]'',''VARCHAR(8000)'')';
--... and use sp_executesql to run this code
EXEC sp_executesql @cmd,N'@XmlData XML',@xmlData=@xmlData;

-您可以在

之后使用通配符和深度搜索(//)来省略名称。
select @xmlData.value('(/CustomerExtract/*//Customer/Name/First)[1]','VARCHAR(8000)')

-您可以在谓词中使用nodeName变量来测试local-name()
-由于<After>处的<Change>,无论如何在任何情况下都需要进行深度搜索(//)来找到<Customer>

DECLARE @nodeName NVARCHAR(100)=N'Delete';
select @xmlData.value('(/CustomerExtract/*[local-name()=sql:variable("@nodeName")]//Customer/Name/First)[1]','VARCHAR(8000)')