sql-servert-sqlsqlxmlxquery-sqlxml.modify

When are curly brackets required when used in a XQuery in SQL Server?


I'm using the modify() and query() methods to interact with XML data.

Here is an example of how I'm using modify():

DECLARE @i int = 1
DECLARE @x xml = N'<?xml version="1.0" encoding="utf-16"?><Table></Table>'

SET @x.modify('insert <Row><Column>{sql:variable("@i")}</Column></Row> as last into (//Table)[1]');

SELECT @x

Here is an example of how I'm using query():

DECLARE @i int = 2
DECLARE @x xml = N'<?xml version="1.0" encoding="utf-16"?>
<Table>
  <Row>
    <Column>1</Column>
  </Row>
  <Row>
    <Column>2</Column>
  </Row>
  <Row>
    <Column>3</Column>
  </Row>
  <Row>
    <Column>4</Column>
  </Row>
</Table>'

DECLARE @xt xml
SET @xt = @x.query('//Table/Row[{sql:variable("@i")}]')

SELECT @xt

I thought curly brackets were required when used in an XQuery, as described at SQLServerCentral here.

However, the curly brackets in the query() method are causing a syntax error that is only resolved by removing them. Alternatively, the curly brackets are just fine when used with modify().

So, could someone clarify the purpose of the curly brackets when used with the sql:variable() function in an XQuery and when they are required?


Solution

  • sql:variable() is a function expression, which means it can only be used in positions where expressions are expected. {} denotes XML construction, this can only be used in places where XML construction is expected.

    In the case of .modify, the syntax insert SomeNewXml as last into /SomeNode expects constructed XML in the place of SomeNewXml. In your case, you have used:

    <Row><Column>{sql:variable("@i")}</Column></Row>
    

    You cannot remove the braces here because otherwise it would try to interpret sql:variable("@i") as raw XML text. The {} escapes it so you can use a dynamic expression to construct the XML.


    In the .query case, you are using it as a predicate filter. If the value is statically known as an xs:integer (which it is here), then it's interpreted as a position predicate.

    Predicate values can only be xs:integer, xs:Boolean, or node*

    So using braces makes no sense:

    //Table/Row[{sql:variable("@i")}]
    

    now it thinks you are trying to pass constructed XML instead of an xs:integer, which is unexpected.


    Note that none of this is specific to sql:variable, it applies any time you use an XQuery expression in such positions.