Usar el modo PATH

Tal como se describe en Generar XML mediante FOR XML, el modo PATH facilita la combinación de elementos y atributos. También facilita la especificación de anidación adicional para representar propiedades complejas. Puede utilizar consultas de modo FOR XML EXPLICIT para generar XML a partir de un conjunto de filas, pero el modo PATH supone una alternativa más sencilla a las consultas de modo EXPLICIT potencialmente complicadas. El modo PATH, junto con la posibilidad de escribir consultas FOR XML anidadas y la directiva TYPE para devolver instancias de tipo xml, permite escribir consultas de forma más fácil.

En el modo PATH, los nombres o alias de columna se tratan como expresiones XPath. Estas expresiones indican el modo en el que se asignan los valores a XML. Cada expresión XPath es una expresión relativa que proporciona el tipo de elemento, como el atributo, el elemento y el valor escalar, así como el nombre y la jerarquía del nodo que se generará en relación con el elemento de fila.

En este tema se describen las condiciones siguientes para la asignación de columnas en un conjunto de filas:

  • Columnas sin nombre
  • Columnas con nombre
  • Columnas con un nombre especificado como carácter comodín (*)
  • Columnas con el nombre de una prueba de nodo XPath
  • Nombres de columna con la ruta de acceso especificada como data()
  • Columnas que incluyen un valor NULL de forma predeterminada

Columnas sin nombre

Las columnas sin nombre se insertarán entre líneas. Por ejemplo, las columnas calculadas o las consultas escalares anidadas que no especifiquen alias de columna generarán columnas sin nombre. Si la columna es de tipo xml, se insertará el contenido de esa instancia de tipo de datos. De lo contrario, el contenido de la columna se insertará como un nodo de texto.

SELECT 2+2
FOR XML PATH

Cree este XML. De forma predeterminada, por cada fila del conjunto de filas, se genera un elemento <row> en el XML resultante. Ocurre lo mismo que en el modo RAW.

<row>4</row>

La consulta siguiente devuelve un conjunto de filas de tres columnas. La tercera columna sin nombre incluye los datos XML. El modo PATH inserta una instancia de tipo xml.

SELECT ProductModelID,
       Name,
       Instructions.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
                /MI:root/MI:Location 
              ') 
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH 
go

Éste es el resultado parcial:

<row>
  <ProductModelID>7</ProductModelID>
  <Name>HL Touring Frame</Name>
  <MI:Location ...LocationID="10" ...></MI:Location>
  <MI:Location ...LocationID="20" ...></MI:Location>
   ...
</row>

Columnas con nombre

A continuación se exponen las condiciones específicas en las que se asignan las columnas del conjunto de filas con un nombre, distinguiendo mayúsculas de minúsculas, al XML resultante:

  • El nombre de la columna empieza con un signo (@)
  • El nombre de la columna no empieza con un signo (@)
  • El nombre de la columna no empieza con un signo @ e incluye una marca de barra diagonal (/)
  • Varias columnas comparten el mismo prefijo
  • Una columna tiene un nombre distinto

El nombre de la columna empieza con un signo (@)

Si el nombre de la columna empieza con un signo (@) y no incluye una marca de barra diagonal (/), se creará un atributo del elemento <row> que tenga el valor de columna correspondiente. Por ejemplo, la consulta siguiente devuelve un conjunto de filas con dos columnas (@PmId y Name). En el XML resultante, se agrega un atributo PmId al elemento <row> correspondiente y se le asigna un valor de ProductModelID.

SELECT ProductModelID as "@PmId",
       Name
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH 
go

El resultado es el siguiente:

<row PmId="7">
  <Name>HL Touring Frame</Name>
</row>

Tenga en cuenta que los atributos deben aparecer antes de cualquier otro tipo de nodo, como los de elemento y texto, del mismo nivel. La consulta siguiente devolverá un error:

SELECT Name,
       ProductModelID as "@PmId"
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH 
go

El nombre de la columna no empieza con un signo (@)

Si el nombre de la columna no empieza con un signo (@), no es una de las pruebas de nodo XPath y no incluye una marca de barra diagonal (/), se creará un elemento XML que es un subelemento del elemento de fila, <row> de forma predeterminada.

La consulta siguiente especifica el nombre de la columna, el resultado. Por tanto, se agrega un elemento secundario <result> al elemento <row>.

SELECT 2+2 as result
for xml PATH

El resultado es el siguiente:

<row>
  <result>4</result>
</row>

La consulta siguiente especifica el nombre de la columna, ManuWorkCenterInformation, para el XML devuelto por la expresión XQuery especificada con la columna Instructions de tipo xml. Por tanto, se agrega un elemento <ManuWorkCenterInformation> como elemento secundario del elemento <row>.

SELECT 
       ProductModelID,
       Name,
       Instructions.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
                /MI:root/MI:Location 
              ') as ManuWorkCenterInformation
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH 
go

El resultado es el siguiente:

<row>
  <ProductModelID>7</ProductModelID>
  <Name>HL Touring Frame</Name>
  <ManuWorkCenterInformation>
    <MI:Location ...LocationID="10" ...></MI:Location>
    <MI:Location ...LocationID="20" ...></MI:Location>
     ...
  </ManuWorkCenterInformation>
</row>

El nombre de la columna no empieza con un signo @ e incluye una marca de barra diagonal (/)

Si el nombre de la columna no empieza con un signo @ pero incluye una marca de barra diagonal (/), el nombre de la columna indica una jerarquía XML. Por ejemplo, si el nombre de la columna es "Name1/Name2/Name3.../Namen ", cada Namei representa un nombre de elemento que está anidado en el elemento de fila actual (para i=1) o que se encuentra debajo del elemento con el nombre Namei-1. Si Namen empieza con '@', se asignará a un atributo del elemento Namen-1.

Por ejemplo, la consulta siguiente devuelve un Id. y un nombre de empleado que están representados como un elemento EmpName complejo que incluye nombre, inicial y apellidos.

SELECT EmployeeID "@EmpID", 
       FirstName  "EmpName/First", 
       MiddleName "EmpName/Middle", 
       LastName   "EmpName/Last"
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.EmployeeID = C.ContactID
AND    E.EmployeeID=1
FOR XML PATH

Los nombres de columna se utilizan como una ruta de acceso en la creación de XML en el modo PATH. El nombre de columna que incluye valores de Id. de empleados empieza con '@'. Por tanto, se agrega un atributo, EmpID, al elemento <row>. Todas las demás columnas incluyen una marca de barra diagonal ('/') en el nombre de columna que indica la jerarquía. El XML resultante tendrá el elemento secundario <EmpName> debajo del elemento <row>, y el elemento secundario <EmpName> tendrá los elementos secundarios <First>, <Middle> y <Last>.

<row EmpID="1">
  <EmpName>
    <First>Gustavo</First>
    <Last>Achong</Last>
  </EmpName>
</row>

El valor de la inicial del empleado es Null y, de forma predeterminada, el valor Null se asigna a la ausencia del elemento o atributo. Si desea que se generen elementos para los valores NULL, puede especificar la directiva ELEMENTS con XSINIL tal y como se muestra en esta consulta.

SELECT EmployeeID "@EmpID", 
       FirstName  "EmpName/First", 
       MiddleName "EmpName/Middle", 
       LastName   "EmpName/Last"
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.EmployeeID = C.ContactID
AND    E.EmployeeID=1
FOR XML PATH, ELEMENTS XSINIL

El resultado es el siguiente:

<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      EmpID="1">
  <EmpName>
    <First>Gustavo</First>
    <Middle xsi:nil="true" />
    <Last>Achong</Last>
  </EmpName>
</row>

De forma predeterminada, el modo PATH genera XML centrado en elementos. Por tanto, la especificación de la directiva ELEMENTS en una consulta de modo PATH no tiene ningún efecto. No obstante, tal y como se muestra en el ejemplo anterior, la directiva ELEMENTS resulta útil con XSINIL a fin de generar elementos para valores Null.

Además del Id. y el nombre, la consulta siguiente recupera una dirección del empleado. Según la ruta de acceso de los nombres de las columnas de direcciones, se agregará un elemento secundario <Address> al elemento <row> y los detalles de la dirección se agregarán como elemento secundario del elemento <Address>.

SELECT EmployeeID   "@EmpID", 
       FirstName    "EmpName/First", 
       MiddleName   "EmpName/Middle", 
       LastName     "EmpName/Last",
       AddressLine1 "Address/AddrLine1",
       AddressLine2 "Address/AddrLIne2",
       City         "Address/City"
FROM   HumanResources.Employee E, Person.Contact C, Person.Address A
WHERE  E.EmployeeID = C.ContactID
AND    E.AddressID = A.AddressID
AND    E.EmployeeID=1
FOR XML PATH

El resultado es el siguiente:

<row EmpID="1">
  <EmpName>
    <First>Gustavo</First>
    <Last>Achong</Last>
  </EmpName>
  <Address>
    <AddrLine1>7726 Driftwood Drive</AddrLine1>
    <City>Monroe</City>
  </Address>
</row>

Varias columnas comparten el mismo prefijo

Si varias columnas consecutivas comparten el mismo prefijo de ruta de acceso, se agruparán con el mismo nombre. Si se utilizan distintos prefijos de espacio de nombres aunque estén enlazados al mismo espacio de nombres, una ruta de acceso se considerará diferente. En la consulta anterior, las columnas FirstName, MiddleName y LastName comparten el mismo prefijo EmpName. Por tanto, se agregarán como elementos secundarios del elemento <EmpName>. Lo mismo ocurrió al crear el elemento <Address> en el ejemplo anterior.

Una columna tiene un nombre distinto

Si aparece una columna con un nombre distinto, interrumpirá la agrupación, tal y como se muestra en la siguiente consulta modificada. La consulta interrumpe la agrupación de FirstName, MiddleName y LastName, tal y como se especifica en la consulta anterior, agregando columnas de dirección entre las columnas FirstName y MiddleName.

SELECT EmployeeID "@EmpID", 
       FirstName "EmpName/First", 
       AddressLine1 "Address/AddrLine1",
       AddressLine2 "Address/AddrLIne2",
       City "Address/City",
       MiddleName "EmpName/Middle", 
       LastName "EmpName/Last"
FROM   HumanResources.EmployeeAddress E, Person.Contact C, Person.Address A
WHERE  E.EmployeeID = C.ContactID
AND    E.AddressID = A.AddressID
AND    E.EmployeeID=1
FOR XML PATH

Como resultado, la consulta crea dos elementos <EmpName>. El primer elemento <EmpName> tiene el elemento secundario <FirstName> y el segundo elemento <EmpName> tiene los elementos secundarios <MiddleName> y <LastName>.

El resultado es el siguiente:

<row EmpID="1">
  <EmpName>
    <First>Gustavo</First>
  </EmpName>
  <Address>
    <AddrLine1>7726 Driftwood Drive</AddrLine1>
    <City>Monroe</City>
  </Address>
  <EmpName>
    <Last>Achong</Last>
  </EmpName>
</row>

Columnas con un nombre especificado como carácter comodín

Si el nombre de columna especificado es un carácter comodín (*), su contenido se insertará como si no se hubiera especificado ningún nombre. Si esta columna no es del tipo xml, su contenido se insertará como un nodo de texto, tal y como se muestra en el ejemplo siguiente:

SELECT EmployeeID "@EmpID", 
       FirstName "*", 
       MiddleName "*", 
       LastName "*"
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.EmployeeID = C.ContactID
AND    E.EmployeeID=1
FOR XML PATH

El resultado es el siguiente:

<row EmpID="1">GustavoAchong</row>

Si la columna es del tipo xml, se insertará el árbol XML correspondiente. Por ejemplo, la consulta siguiente especifica "*" para el nombre de columna que incluye el XML devuelto por XQuery con la columna Instructions.

SELECT 
       ProductModelID,
       Name,
       Instructions.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
                /MI:root/MI:Location 
              ') as "*"
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH 
go

Éste es el resultado. El XML devuelto por XQuery se insertará sin un elemento de ajuste.

<row>
  <ProductModelID>7</ProductModelID>
  <Name>HL Touring Frame</Name>
  <MI:Location LocationID="10">...</MI:Location>
  <MI:Location LocationID="20">...</MI:Location>
...
</row>

Columnas con el nombre de una prueba de nodo XPath

Si el nombre de columna es una de las pruebas de nodo XPath, se asignará el contenido tal y como se muestra en la tabla siguiente.

Cuando el nombre de la columna es una prueba de nodo XPath, se asigna el contenido al nodo correspondiente. Si el tipo SQL de la columna es xml, se devolverá un error.

Nombre de columna Comportamiento

text()

En el caso de las columnas con el nombre text(), el valor de cadena de la misma se agregará como un nodo de texto.

comment()

En el caso de las columnas con el nombre comment(), el valor de cadena de la misma se agregará como un comentario XML.

node()

En el caso de las columnas con el nombre node(), el resultado será el mismo que cuando el nombre de la columna es un carácter comodín (*).

processing-instruction(name)

En el caso de las columnas con el nombre de una instrucción de procesamiento, su valor de cadena se agregará como el valor PI para el nombre de destino de la instrucción de procesamiento.

Nombres de columna con la ruta de acceso especificada como data()

Si la ruta de acceso especificada como nombre de la columna es "data()", el valor se tratará como un valor atómico en el XML generado. Si el siguiente elemento de la serialización también es un valor atómico, se agregará un carácter de espacio al XML. Esto resulta útil cuando se crean valores de elementos y atributos del tipo lista. La consulta siguiente recupera el Id. del modelo de producto, el nombre del producto y una lista de los productos de ese modelo.

SELECT ProductModelID       as "@ProductModelID",
       Name                 as "@ProductModelName",
      (SELECT ProductID as "data()"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
      FOR XML PATH (''))    as "@ProductIDs"
FROM  Production.ProductModel
WHERE ProductModelID= 7 
FOR XML PATH('ProductModelData')

La instrucción SELECT anidada recupera una lista de Id. de productos. Especificará "data()" como nombre de columna de los Id. de productos. El modo PATH especifica una cadena vacía para el nombre de elemento de fila, por lo que no se generará ningún elemento de fila. En su lugar, se devolverán los valores como asignados a un atributo ProductIDs del elemento de fila <ProductModelData> de la instrucción SELECT primaria. El resultado es el siguiente:

<ProductModelData ProductModelID="7" 
                  ProductModelName="HL Touring Frame" 
                  ProductIDs="885 887 888 889 890 891 892 893" />

Columnas que incluyen un valor NULL de forma predeterminada

De forma predeterminada, un valor NULL en una columna se asigna a la ausencia del atributo, nodo u elemento. Este comportamiento predeterminado se puede sobrescribir solicitando XML centrado en elementos mediante la directiva ELEMENTS y especificando XSINIL a fin de solicitar la adición de elementos para valores NULL, tal y como se muestra en la consulta siguiente:

SELECT EmployeeID as "@EmpID", 
       FirstName  as "EmpName/First", 
       MiddleName as "EmpName/Middle", 
       LastName   as "EmpName/Last"
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.EmployeeID = C.ContactID
AND    E.EmployeeID=1
FOR XML PATH, ELEMENTS XSINIL

A continuación se muestra el resultado. Tenga en cuenta que si no se especifica XSINIL, el elemento <Middle> estará ausente.

<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" EmpID="1">
  <EmpName>
    <First>Gustavo</First>
    <Middle xsi:nil="true" />
    <Last>Achong</Last>
  </EmpName>
</row>

Compatibilidad con elementos de espacio de nombres

Esta versión incluye la compatibilidad con elementos de espacio de nombres en el modo PATH mediante WITH NAMESPACES. La consulta siguiente, por ejemplo, muestra la sintaxis de WITH NAMESPACES para declarar un espacio de nombres ("a:") que luego puede usarse en la instrucción SELECT subsiguiente:

WITH XMLNAMESPACES('a' as a)
SELECT 1 as 'a:b'
FOR XML PATH

Ejemplos

Estas muestras ilustran la utilización del modo PATH en la creación de XML a partir de una consulta SELECT. Muchas de estas consultas se especifican utilizando los documentos XML de instrucciones de fabricación de bicicletas almacenados en la columna Instructions de la tabla ProductModel. Para obtener más información acerca de las instrucciones XML, vea Representación de tipo de datos xml en la base de datos AdventureWorks.

A. Especificar una consulta sencilla de modo PATH

Esta consulta especifica un modo FOR XML PATH.

SELECT 
       ProductModelID,
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML PATH
go

El resultado siguiente es XML centrado en elementos en el que cada valor de columna del conjunto de filas resultante se agrupa en un elemento. Puesto que la cláusula SELECT no especifica ningún alias para los nombres de columna, los nombres de elemento secundario generados son los mismos que los nombres de columna correspondientes de la cláusula SELECT. Para cada fila del conjunto de filas se agrega una etiqueta <row>.

<row>
  <ProductModelID>122</ProductModelID>
  <Name>All-Purpose Bike Stand</Name>
</row>
<row>
  <ProductModelID>119</ProductModelID>
  <Name>Bike Wash</Name>
</row>

El resultado siguiente es el mismo que el de la consulta de modo RAW con la opción ELEMENTS especificada. Devuelve XML centrado en elementos con un elemento <row> predeterminado para cada fila del conjunto de resultados.

SELECT ProductModelID,
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML RAW, ELEMENTS

También puede especificar el nombre del elemento de fila para sobrescribir el valor <row> predeterminado. Por ejemplo, la consulta siguiente devuelve el elemento <ProductModel> para cada fila del conjunto de filas.

SELECT ProductModelID,
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML PATH ('ProductModel')
Go

El XML resultante tendrá un nombre de elemento de fila especificado.

<ProductModel>
  <ProductModelID>122</ProductModelID>
  <Name>All-Purpose Bike Stand</Name>
</ProductModel>
<ProductModel>
  <ProductModelID>119</ProductModelID>
  <Name>Bike Wash</Name>
</ProductModel>

Si especifica una cadena de longitud cero, no se generará el elemento de ajuste.

SELECT ProductModelID,
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML PATH ('')
Go

El resultado es el siguiente:

<ProductModelID>122</ProductModelID>
<Name>All-Purpose Bike Stand</Name>
<ProductModelID>119</ProductModelID>
<Name>Bike Wash</Name>

B. Especificar nombres de columna de tipo XPath

En la consulta siguiente, el nombre de columna ProductModelID especificado empieza con '@' y no incluye una marca de barra diagonal ('/'). Por tanto, se creará en el XML resultante un atributo del elemento <row> que tenga el valor de columna correspondiente.

SELECT ProductModelID as "@id",
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML PATH ('ProductModelData')
go

El resultado es el siguiente:

< ProductModelData id="122">
    <Name>All-Purpose Bike Stand</Name>
</ ProductModelData >
< ProductModelData id="119">
    <Name>Bike Wash</Name>
</ ProductModelData >

Puede agregar un solo elemento de nivel superior especificando la opción root en FOR XML.

SELECT ProductModelID as "@id",
       Name
FROM Production.ProductModel
WHERE ProductModelID=122 or ProductModelID=119
FOR XML PATH ('ProductModelData'), root ('Root')
go

Para generar una jerarquía, puede incluir sintaxis del tipo PATH. Por ejemplo, si cambia el nombre de la columna Name por "SomeChild/ModelName", obtendrá XML con una jerarquía, tal y como se muestra en este resultado:

<Root>
  <ProductModelData id="122">
    <SomeChild>
      <ModelName>All-Purpose Bike Stand</ModelName>
    </SomeChild>
  </ProductModelData>
  <ProductModelData id="119">
    <SomeChild>
      <ModelName>Bike Wash</ModelName>
    </SomeChild>
  </ProductModelData>
</Root>

Además del Id. y el nombre del modelo de producto, la consulta siguiente recupera las ubicaciones con instrucciones de fabricación para el modelo de producto. Puesto que la columna Instructions es de tipo xml, se especifica el método query() del tipo de datos xml para recuperar la ubicación.

SELECT ProductModelID as "@id",
       Name,
       Instructions.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
                /MI:root/MI:Location 
              ') as ManuInstr
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH ('ProductModelData'), root ('Root')
go

El resultado parcial es el siguiente. Puesto que la consulta especifica ManuInstr como nombre de columna, el XML devuelto por el método query() se ajusta en una etiqueta <ManuInstr> tal y como se muestra a continuación:

<Root>
  <ProductModelData id="7">
    <Name>HL Touring Frame</Name>
    <ManuInstr>
      <MI:Location xmlns:MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions" 
        <MI:step>...</MI:step>...
      </MI:Location>
      ...
    </ManuInstr>
  </ProductModelData>
</Root>

En la consulta FOR XML anterior, puede incluir espacios de nombres para los elementos <Root> y <ProductModelData>. Para ello, defina en primer lugar el prefijo de enlace de espacios de nombres mediante WITH XMLNAMESPACES y prefijos en la consulta FOR XML. Para obtener más información, vea Agregar espacios de nombres mediante WITH XMLNAMESPACES.

WITH XMLNAMESPACES (
   'uri1' as ns1,  
   'uri2' as ns2,
   'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' as MI)
SELECT ProductModelID as "ns1:ProductModelID",
       Name           as "ns1:Name",
       Instructions.query('
                /MI:root/MI:Location 
              ') 
FROM Production.ProductModel
WHERE ProductModelID=7
FOR XML PATH ('ns2:ProductInfo'), root('ns1:root')
go

Tenga en cuenta que el prefijo MI también se define en WITH XMLNAMESPACES. Como resultado, el método query() del tipo xml especificado no define el prefijo en el prólogo de la consulta. El resultado es el siguiente:

<ns1:root xmlns:MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions" xmlns="uri2" xmlns:ns2="uri2" xmlns:ns1="uri1">
  <ns2:ProductInfo>
    <ns1:ProductModelID>7</ns1:ProductModelID>
    <ns1:Name>HL Touring Frame</ns1:Name>
    <MI:Location xmlns:MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions" 
       LaborHours="2.5" LotSize="100" MachineHours="3" SetupHours="0.5" LocationID="10" >
       <MI:step>
          Insert <MI:material>aluminum sheet MS-2341</MI:material> into the <MI:tool>T-85A framing tool</MI:tool>. 
       </MI:step>
         ...
    </MI:Location>
     ...
  </ns2:ProductInfo>
</ns1:root>

C. Generar una lista de valores con el modo PATH

Esta consulta crea una lista de valores de Id. de productos para cada modelo de producto. Además, crea elementos anidados <ProductName> para cada Id. de producto, tal y como se muestra en este fragmento de XML:

<ProductModelData ProductModelID="7" ProductModelName="..."  
                  ProductIDs="product id list in the product model" >
  <ProductName>...</ProductName>
  <ProductName>...</ProductName>
  ...
</ProductModelData>

Ésta es la consulta que crea el XML deseado:

SELECT ProductModelID     as "@ProductModelID",
       Name               as "@ProductModelName",
      (SELECT ProductID as "data()"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
       FOR XML PATH ('')) as "@ProductIDs",
       (SELECT Name as "ProductName"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
        FOR XML PATH ('')) as "ProductNames"
FROM   Production.ProductModel
WHERE  ProductModelID= 7 or ProductModelID=9
FOR XML PATH('ProductModelData')

Observe lo siguiente en la consulta anterior:

  • El primer SELECT anidado devuelve una lista de ProductID utilizando data() como nombre de columna. La consulta especifica una cadena vacía como nombre del elemento de fila en FOR XML PATH, por lo que no se generará ningún elemento. En su lugar, se asignará la lista de valores al atributo ProductID.
  • El segundo SELECT anidado recupera nombres para los productos del modelo de producto. Genera elementos <ProductName> que se devuelven ajustados en el elemento <ProductNames>, puesto que la consulta especifica ProductNames como nombre de columna.

Éste es el resultado parcial:

<ProductModelData PId="7" 
                  ProductModelName="HL Touring Frame" 
                  ProductIDs="885 887 ...">
  <ProductNames>
    &lt;ProductName&gt;HL Touring Frame - Yellow, 60&lt;/ProductName&gt;
    &lt;ProductName&gt;HL Touring Frame - Yellow, 46&lt;/ProductName&gt;</ProductNames>
    ...
</ProductModelData>
<ProductModelData PId="9" 
                  ProductModelName="LL Road Frame" 
                  ProductIDs="722 723 724 ...">
  <ProductNames>
     &lt;ProductName&gt;LL Road Frame - Black, 58&lt;/ProductName&gt;
     &lt;ProductName&gt;LL Road Frame - Black, 60&lt;/ProductName&gt;
     &lt;ProductName&gt;LL Road Frame - Black, 62&lt;/ProductName&gt;
     ...
  </ProductNames>
</ProductModelData>

La subconsulta que crea los nombres de producto devuelve el resultado como una cadena para la que se crea una entidad y que a continuación se agrega al XML. Si agrega la directiva TYPE, FOR XML PATH (''), type, la subconsulta devolverá el resultado como un tipo xml y no se creará ninguna entidad.

SELECT ProductModelID as "@ProductModelID",
      Name as "@ProductModelName",
      (SELECT ProductID as "data()"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
       FOR XML PATH ('')
       ) as "@ProductIDs",
       (
       SELECT Name as "ProductName"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
       FOR XML PATH (''), type
       ) as "ProductNames"
       
FROM Production.ProductModel
WHERE ProductModelID= 7 or ProductModelID=9
FOR XML PATH('ProductModelData')

D. Agregar espacios de nombres en el XML resultante

Tal y como se describe en el tema Agregar espacios de nombres mediante WITH XMLNAMESPACES, puede utilizar WITH XMLNAMESPACES para incluir espacios de nombres en las consultas de modo PATH. Por ejemplo, los nombres especificados en la cláusula SELECT incluyen prefijos de espacio de nombres. La siguiente consulta de modo PATH crea XML con espacios de nombres.

SELECT 'en'    as "English/@xml:lang",
       'food'  as "English",
       'ger'   as "German/@xml:lang",
       'Essen' as "German"
FOR XML PATH ('Translation')
go

El atributo @xml:lang agregado al elemento <English> se define en el espacio de nombres xml predefinido.

El resultado es el siguiente:

<Translation>
  <English xml:lang="en">food</English>
  <German xml:lang="ger">Essen</German>
</Translation>

La consulta siguiente es parecida al ejemplo C, con la diferencia de que utiliza WITH XMLNAMESPACES para incluir espacios de nombres en el XML resultante. Para obtener más información, vea Agregar espacios de nombres mediante WITH XMLNAMESPACES.

WITH XMLNAMESPACES ('uri1' as ns1,  DEFAULT 'uri2')
SELECT ProductModelID as "@ns1:ProductModelID",
      Name as "@ns1:ProductModelName",
      (SELECT ProductID as "data()"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
       FOR XML PATH ('')
       ) as "@ns1:ProductIDs",
       (
       SELECT ProductID as "@ns1:ProductID", 
              Name as "@ns1:ProductName"
       FROM   Production.Product
       WHERE  Production.Product.ProductModelID = 
              Production.ProductModel.ProductModelID
       FOR XML PATH , type 
       ) as "ns1:ProductNames"
       
FROM Production.ProductModel
WHERE ProductModelID= 7 or ProductModelID=9
FOR XML PATH('ProductModelData'), root('root')

El resultado es el siguiente:

<root xmlns="uri2" xmlns:ns1="uri1">
  <ProductModelData ns1:ProductModelID="7" ns1:ProductModelName="HL Touring Frame" ns1:ProductIDs="885 887 888 889 890 891 892 893">
    <ns1:ProductNames>
      <row xmlns="uri2" xmlns:ns1="uri1" ns1:ProductID="885" ns1:ProductName="HL Touring Frame - Yellow, 60" />
      <row xmlns="uri2" xmlns:ns1="uri1" ns1:ProductID="887" ns1:ProductName="HL Touring Frame - Yellow, 46" />
       ...
    </ns1:ProductNames>
  </ProductModelData>
  <ProductModelData ns1:ProductModelID="9" ns1:ProductModelName="LL Road Frame" ns1:ProductIDs="722 723 724 725 726 727 728 729 730 736 737 738">
    <ns1:ProductNames>
      <row xmlns="uri2" xmlns:ns1="uri1" ns1:ProductID="722" ns1:ProductName="LL Road Frame - Black, 58" />
        ...
    </ns1:ProductNames>
  </ProductModelData>
</root>

Vea también

Referencia

Generar XML mediante FOR XML

Conceptos

Agregar espacios de nombres mediante WITH XMLNAMESPACES

Otros recursos

SELECT (Transact-SQL)

Ayuda e información

Obtener ayuda sobre SQL Server 2005