以下代码有效,并且输出名字:
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 /
有人可以解释一下为什么会发生这种情况吗?什么是实现我想要的结果的正确语法?谢谢
答案 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)')