Prácticas recomendadas para XML

SQL Server 2005 ofrece un amplio nivel de compatibilidad para el procesamiento de datos XML. Los valores XML se pueden almacenar de forma nativa en una columna de tipo de datos xml cuyo tipo se puede asignar de acuerdo con una colección de esquemas XML o que puede dejarse sin tipo. Es posible indizar la columna XML. Además, se admite la manipulación de datos con precisión mediante XQuery y XML DML. XML DML es una extensión para la modificación de datos.

Las versiones SQL Server 2000 y SQLXML Web proporcionan funciones muy completas para la administración de datos XML. Estas características se centran en las asignaciones entre datos relacionales y XML. Las vistas XML de datos relacionales se pueden definir mediante el uso de XSD anotado (AXSD) para proporcionar un enfoque centrado en XML que admita las capacidades de carga masiva de datos, consultas y actualización de datos XML. Las extensiones Transact-SQL ofrecen un enfoque centrado en SQL para la asignación de los resultados de consultas relacionales a XML mediante el uso de FOR XML, y para la generación de vistas relacionales de XML mediante OPENXML. Estas compatibilidades se han ampliado en SQL Server 2005. Además de la recién incorporada compatibilidad con XML nativo, SQL Server 2005 proporciona una plataforma eficaz para programar aplicaciones completas para la administración de datos semiestructurados y no estructurados.

En este tema se ofrecen directrices para crear modelos de datos XML y usarlos en SQL Server 2005. Está compuesto por las secciones siguientes:

  • Crear modelos de datos
    Los datos XML se pueden almacenar de muchas formas en SQL Server 2005 utilizando tipos de datos xml nativos y XML dividido en tablas. En este tema se ofrecen directrices para hacer las elecciones apropiadas para crear modelos de datos XML. También se trata la indización de datos XML, la promoción de propiedades y la escritura de instancias XML.
  • Uso
    En esta sección, se tratan temas relacionados con el uso, como la carga de datos XML en el servidor y la inferencia de tipos en la compilación de consultas. En esta sección también se explican y se diferencian características muy relacionadas, y se sugiere el uso apropiado de éstas. Todo ello se ilustra con ejemplos.

Crear modelos de datos

En esta sección se esbozan los motivos por los que se debe usar XML en SQL Server 2005. La sección también ofrece directrices para elegir entre tecnología de almacenamiento de XML nativo y de vistas XML, así como sugerencias para crear modelos de datos.

Modelo de datos relacionales o XML

Si los datos están muy estructurados con un esquema conocido, el modelo relacional tiene más probabilidades de funcionar mejor para el almacenamiento de datos. SQL Server proporciona la funcionalidad y las herramientas necesarias. Por otra parte, si los datos están semiestructurados o no están estructurados, o no se conoce su estructura, debe contemplar la posibilidad de crear un modelo para los datos.

XML es una buena opción si desea un modelo independiente de la plataforma para garantizar la portabilidad de los datos mediante el uso de marcado estructural y semántico. Además, es una opción apropiada si se cumplen algunas de las siguientes propiedades:

  • Los datos están dispersos o no se conoce la estructura de los mismos, o la estructura de los datos puede cambiar de manera importante en el futuro.
  • Los datos representan una jerarquía de inclusión, en lugar de referencias entre entidades, y pueden ser recursivos.
  • El orden es inherente a los datos.
  • Desea realizar consultas en los datos o actualizar parte de ellos, basándose en su estructura.

Si no se cumple ninguna de estas condiciones, debe utilizar el modelo de datos relacional. Por ejemplo, si los datos tienen formato XML pero la aplicación sólo utiliza la base de datos para almacenar y recuperar los datos, sólo necesitará una columna [n]varchar(max). Almacenar los datos en una columna XML tiene más ventajas. Una de ellas es que se puede hacer que el motor determine si los datos tienen un formato correcto o son válidos, y otra es la compatibilidad con consultas detalladas y actualizaciones en los datos XML.

Razones para almacenar datos XML en SQL Server 2005

A continuación, se indican algunas de las razones para usar características XML nativas en SQL Server 2005 en lugar de administrar los datos XML en el sistema de archivos:

  • Desea compartir los datos XML, hacer consultas en ellos y modificarlos de forma eficaz y con transacciones. El acceso a datos con precisión es importante para la aplicación. Por ejemplo, tal vez desee extraer alguna sección de un documento XML, o insertar una nueva sección sin reemplazar todo el documento.
  • Tiene datos relacionales y datos XML y desea que ambos tipos de datos puedan interoperar dentro de la aplicación.
  • Necesita compatibilidad con lenguajes para realizar consultas y modificar datos en aplicaciones situadas en diversos dominios.
  • Desea que el servidor garantice que los datos tienen un formato correcto y además, opcionalmente, validar los datos de acuerdo con esquemas XML.
  • Desea indizar los datos XML para obtener un procesamiento eficiente de las consultas y una buena escalabilidad, y el uso de un optimizador de consultas de primera clase.
  • Desea el acceso de SOAP, ADO.NET y OLE DB a los datos XML.
  • Desea utilizar la funcionalidad administrativa del servidor de base de datos para administrar los datos XML, por ejemplo, para realizar copias de seguridad, recuperaciones y réplicas.

Si no se satisface ninguna de estas condiciones, es posible que convenga almacenar los datos como tipo de objeto grande no XML, por ejemplo, [n]varchar(max) o varbinary(max).

Opciones de almacenamiento de XML

Las opciones de almacenamiento para XML en SQL Server 2005 son las siguientes:

  • Almacenamiento nativo como tipo de datos xml.
    Los datos se almacenan en una representación interna que conserva el contenido XML de los datos. Incluye jerarquía de contenedores, ordenación de documentos y valores de elementos y atributos. En concreto, se preserva el contenido InfoSet de los datos XML. Para obtener más información acerca de InfoSet, vea http://www.w3.org/TR/xml-infoset. El contenido de InfoSet no tiene por qué ser una copia idéntica del texto XML, porque no se retiene la información siguiente: espacios en blanco insignificantes, orden de atributos, prefijos de espacios de nombres y declaración XML.
    En el caso del tipo de datos xml con tipo, un tipo de datos xml enlazado a esquemas XML, el contenido InfoSet de validación de esquema posterior (PSVI) agrega información del tipo a InfoSet y se codifica en la representación interna. De este modo, se mejora considerablemente la velocidad de análisis. Para obtener más información, vea las especificaciones de esquema XML de W3C en http://www.w3.org/TR/xmlschema-1 y http://www.w3.org/TR/xmlschema-2.
  • Asignar entre almacenamiento XML y relacional
    El esquema anotado (AXSD) permite descomponer el código XML en columnas en una o más tablas. Así, se preserva la fidelidad de los datos en el nivel relacional. Como resultado, la estructura jerárquica se mantiene aunque se omita el orden entre los elementos. El esquema no puede ser recursivo.
  • Almacenamiento de objetos grandes, [n]varchar(max) y varbinary(max)
    Se almacena una copia idéntica de los datos. Esto resulta útil en el caso de aplicaciones para fines específicos, como documentos legales. La mayoría de las aplicaciones no requieren una copia exacta y les basta con el contenido XML (fidelidad InfoSet).

En general, es posible que se tenga que usar una combinación de estos enfoques. Por ejemplo, tal vez desee almacenar los datos XML en una columna de tipo de datos xml y promover las propiedades correspondientes en columnas relacionales. O tal vez desee usar tecnología de asignaciones para almacenar partes no recursivas en columnas no XML y sólo las partes recursivas en columnas de tipo de datos xml .

Elección de la tecnología XML

La elección de la tecnología XML, XML nativo frente a vista XML, en general depende de los siguientes factores:

  • Opciones de almacenamiento
    Los datos XML pueden ser más apropiados para el almacenamiento de objetos grandes (por ejemplo, el manual de un producto) o más sensibles al almacenamiento en columnas relacionales (por ejemplo, un elemento de línea convertido a XML). Cada opción de almacenamiento preserva la fidelidad del documento en distinta medida.
  • Funciones de consultas
    Es posible que una opción de almacenamiento le parezca más apropiada que otra en función de la naturaleza de las consultas y del nivel de detalle con que desea consultar los datos XML. La consulta detallada de los datos XML (por ejemplo, evaluación de predicados en nodos XML) se admite en diversos grados en las dos opciones de almacenamiento.
  • Indizar datos XML
    Tal vez desee indizar los datos XML para acelerar el rendimiento de las consultas XML. Las opciones de indización varían según las opciones de almacenamiento; es necesario hacer la elección apropiada para optimizar la carga de trabajo.
  • Funciones para la modificación de datos
    Algunas cargas de trabajo implican una modificación detallada de los datos XML. Éste es el caso de agregar una sección nueva a un documento. Sin embargo, otras cargas de trabajo, como el contenido Web, no implican la modificación detallada de los datos XML. La compatibilidad con el lenguaje de modificación de datos puede ser importante para la aplicación.
  • Compatibilidad con esquemas
    Los datos XML se pueden describir mediante un esquema que puede ser o no un documento de esquema XML. La compatibilidad con XML enlazado a un esquema depende de la tecnología XML.

Las diferentes opciones también tienen distintas características en cuanto al rendimiento.

Almacenamiento de XML nativo

Los datos XML se pueden almacenar en una columna de tipo de datos xml en el servidor. Esta es una elección apropiada si se cumple lo siguiente:

  • Desea una manera directa de almacenar los datos XML en el servidor y, al mismo tiempo, preservar el orden y la estructura de los documentos.
  • Puede tener o no un esquema para los datos XML.
  • Desea consultar y modificar los datos XML.
  • Desea indizar los datos XML para procesar más rápido las consultas.
  • La aplicación necesita vistas de catálogo del sistema para administrar los datos XML y los esquemas XML.

El almacenamiento XML nativo es útil cuando se tienen documentos XML con una serie de estructuras, o si se tienen documentos XML que se ajustan a esquemas diferentes o completos que son demasiado difíciles de asignar a estructuras relacionales.

Ejemplo: crear modelos de datos XML mediante el tipo de datos xml

Piense en el manual de un producto en formato XML compuesto por un capítulo independiente para cada tema y por varias secciones dentro de cada capítulo. Una sección puede contener subsecciones. Como resultado, <sección> es un elemento recursivo. Los manuales de productos contienen una gran cantidad de contenido mixto, diagramas y material técnico; los datos están semiestructurados. Es posible que los usuarios deseen efectuar búsquedas contextuales de temas de interés, como la sección sobre "índices agrupados" en el capítulo sobre "indización", y consultar dimensiones técnicas.

Un modelo de almacenamiento apropiado para los documentos XML es una columna de tipo de datos xml. Así se preserva el contenido InfoSet de los datos XML. La indización de la columna XML favorece el rendimiento de la consulta.

Ejemplo: retener copias exactas de datos XML

A modo de ilustración, suponga que las normativas del gobierno le exigen que retenga copias textuales exactas de sus documentos XML, como documentos firmados, documentos legales o pedidos de transacciones de almacén. Tal vez desee almacenar los documentos en una columna [n]varchar(max).

Para realizar consultas, convierta los datos al tipo de datos xml en tiempo de ejecución y ejecute Xquery. La conversión en tiempo de ejecución puede ser larga, especialmente si el documento es grande. Si realiza consultas frecuentes, puede almacenar repetidamente los documentos en una columna de tipo de datos xml e indizarla mientras devuelve copias exactas de los documentos desde la columna [n]varchar(max).

La columna XML puede ser una columna calculada basada en la columna [n]varchar(max). Sin embargo, no se puede crear un índice XML en una columna XML calculada, ni se puede generar un índice XML en columnas [n]varchar(max) o varbinary(max).

Tecnología de vistas XML

La definición de una asignación entre los esquemas XML y las tablas de una base de datos permite crear una "vista XML" de los datos permanentes. Se puede efectuar una carga masiva de XML para rellenar las tablas subyacentes mediante la vista XML. Puede efectuar una consulta en la vista XML mediante XPath versión 1.0; la consulta se traduce en consultas SQL en las tablas. Del mismo modo, las actualizaciones también se propagan a dichas tablas.

Esta tecnología es útil en las situaciones siguientes:

  • Desea tener un modelo de programación centrado en XML utilizando vistas XML sobre los datos relacionales existentes.
  • Tiene un esquema (XSD, XDR) para los datos XML que puede haberle proporcionado un asociado externo.
  • El orden no es importante para los datos, los datos de la tabla de la consulta no son recursivos, o el grado máximo de recursividad se conoce de antemano.
  • Desea consultar y modificar los datos a través de la vista XML mediante XPath versión 1.0.
  • Desea efectuar una carga masiva de datos XML y distribuirlos en las tablas subyacentes mediante la vista XML.

Algunos ejemplos son datos relacionales expuestos como XML para el intercambio de datos y servicios Web, y datos XML con esquema fijo. Para obtener más información, vea la biblioteca en línea MSDN Library.

Ejemplo: crear un modelo de datos utilizando un esquema XML anotado (AXSD)

A modo de ilustración, suponga que tiene datos relacionales como clientes, pedidos y artículos de línea, que desea tratar como XML. Defina una vista XML utilizando AXSD sobre los datos relacionales. La vista XML permite efectuar una carga masiva de datos XML en las tablas así como consultar y actualizar los datos relacionales utilizando dicha vista. Este modelo es útil si hay que intercambiar datos que contienen marcado XML con otras aplicaciones, mientras las aplicaciones SQL se ejecutan ininterrumpidamente.

Modelo híbrido

Con frecuencia, para crear modelos de datos, resulta apropiada una combinación de columnas de tipo de datos relacionales y xml . Algunos valores de los datos XML se pueden almacenar en columnas relacionales y, el resto, o el conjunto de valores XML, en una columna XML. De este modo, se puede obtener un mejor rendimiento ya que se tiene más control sobre los índices creados en las columnas relacionales y las características de bloqueo.

Los valores para almacenar en columnas relacionales dependen de la carga de trabajo. Por ejemplo, si se recuperan todos los valores XML basados en la expresión de ruta de acceso /Customer/@CustId, promoviendo el valor del atributo CustId a una columna relacional e indizándolo, se puede lograr un mejor rendimiento en las consultas. Por otra parte, si los datos XML se distribuyen ampliamente y sin redundancias en columnas relacionales, el costo del reensamblado puede ser importante.

En el caso de datos XML muy estructurados, por ejemplo, el contenido de una tabla se ha convertido en XML; se pueden asignar todos los valores a columnas relacionales y, posiblemente, usar la tecnología de vistas XML.

Crear modelos de datos mediante el tipo de datos xml

En esta sección se tratan aspectos sobre creación de modelos de datos para el almacenamiento de XML nativo: Se incluye la indización de datos XML, la promoción de propiedades y el tipo de datos xml con tipo.

La misma tabla o una tabla diferente

Es posible crear una columna de tipo de datos xml en una tabla que contenga otras columnas relacionales o en una tabla aparte con una relación de clave externa con una tabla principal.

Puede crear una columna de tipo de datos xml en la misma tabla si se cumple una de las condiciones siguientes:

  • La aplicación efectúa una recuperación de datos en la columna XML y no requiere un índice XML en la columna XML.
  • Desea generar un índice XML en la columna de tipo de datos xml y la clave principal de la tabla principal es la misma que su clave de agrupación. Para obtener más información, vea la sección sobre la indización de una columna de tipo de datos xml.

Puede crear la columna de tipo de datos xml en una tabla aparte si se cumplen las condiciones siguientes:

  • Desea generar un índice XML en la columna de tipo de datos xml, pero la clave principal de la tabla principal es distinta de su clave de agrupación, la tabla principal no tiene una clave principal, o la tabla principal es un montón (sin clave de agrupación). Esto puede ser cierto si la tabla principal ya existe.
  • No desea que se ralenticen los recorridos de las tablas por la presencia de la columna XML en la tabla. Ésta usa espacio independientemente de si está o no almacenada de manera consecutiva.

Granularidad de los datos XML

La granularidad de los datos XML almacenados en una columna XML es muy importante para bloqueos y, en menor medida, para las actualizaciones. SQL Server usa el mismo mecanismo de bloqueo tanto para datos XML como no XML. Por lo tanto, un bloqueo en el nivel de fila provoca que todas las instancias XML de la fila queden bloqueadas. Cuando la granularidad es grande, el bloqueo de instancias XML grandes provoca una disminución del rendimiento en un escenario multiusuario. Por otra parte, una distribución amplia provoca una pérdida de la encapsulación de objetos e incrementa el costo del reensamblado.

Para lograr un buen diseño, es importante alcanzar un equilibrio entre los requisitos necesarios para crear modelos de datos y las características de bloqueo y actualización. Sin embargo, en SQL Server 2005, el tamaño de las instancias XML almacenadas realmente no es tan importante.

Por ejemplo, las actualizaciones de una instancia XML se efectúan utilizando el nuevo soporte para actualizaciones parciales de objetos binarios grandes (BLOB) y de índices en las que la instancia XML almacenada existente se compara con su versión actualizada. La actualización parcial de objetos binarios grandes (BLOB) realiza una comparación diferencial entre las dos instancias XML y únicamente actualiza las diferencias. Las actualizaciones parciales de índices sólo modifican aquellas filas que se deben cambiar en el índice XML.

Tipo de datos xml sin tipo, con tipo y restringido

En SQL Server 2005, el tipo de datos xml implementa el tipo de datos xml del estándar SQL-2003 de ISO. Por lo tanto, puede almacenar documentos XML versión 1.0 correctos, así como los llamados fragmentos de contenido XML con nodos de texto y un número arbitrario de elementos de nivel superior en una columna XML sin tipo. El sistema comprueba que todos los datos tienen un formato correcto, no requiere que la columna esté enlazada a esquemas XML y rechaza los datos que no tienen un formato correcto en sentido amplio. Esto también se cumple para parámetros y variables XML sin tipo.

Si tiene esquemas XML para describir los datos XML, puede asociar estos esquemas a la columna XML para obtener XML con tipo. Los esquemas XML se utilizan para validar los datos, efectuar comprobaciones más precisas durante la compilación de instrucciones de consulta y de modificación que con el XML sin tipo, y optimizar el procesamiento del almacenamiento y de las consultas.

El tipo de datos xml sin tipo se emplea en las siguientes situaciones:

  • No tiene un esquema para los datos XML.
  • Tiene esquemas pero no desea que el servidor valide los datos. Esto a veces ocurre cuando una aplicación realiza la validación en el cliente antes de almacenar los datos en el servidor, almacena temporalmente datos XML que no son válidos según el esquema, o utiliza componentes del esquema no compatibles con el servidor (por ejemplo, key/keyref).

El tipo de datos xml con tipo se emplea en las siguientes situaciones:

  • Tiene esquemas para los datos XML y desea que el servidor valide estos datos según los esquemas XML.
  • Desea aprovechar las optimizaciones del almacenamiento y de las consultas en función de la información del tipo.
  • Desea aprovechar mejor la información del tipo durante la compilación de las consultas.

Columnas, parámetros y variables XML con tipo pueden almacenar documentos o contenido XML. No obstante, hay que especificar con un indicador si se va a almacenar un documento o contenido en el momento de la declaración. Además, hay que proporcionar la colección de esquemas XML. Especifique DOCUMENT si cada instancia XML tiene exactamente un elemento de nivel superior. En caso contrario, use CONTENT. El compilador de consultas usa el indicador DOCUMENT en comprobaciones de tipo durante la compilación de consultas para inferir elementos singleton de nivel superior.

Además de asignar tipo a una columna XML, puede usar restricciones relacionales (de fila o de columna) en columnas de tipo de datos xml con o sin tipo. Las restricciones se emplean en estas situaciones:

  • No se pueden expresar las reglas de su empresa en esquemas XML. Por ejemplo, la dirección de entrega de una floristería debe estar a menos de 75 kilómetros de su ubicación. Esto se puede escribir como una restricción en la columna XML. La restricción puede implicar métodos de tipo de datos xml.
  • La restricción implica a otras columnas XML o no XML de la tabla. Un ejemplo es que se fuerce el Id. de un cliente (/Customer/@CustId) encontrado en una instancia XML para que coincida con el valor encontrado en una columna relacional CustomerID.

Definición de tipo de documento (DTD)

Las columnas de tipo de datos xml, las variables y los parámetros pueden obtener tipos utilizando un esquema XML, pero no mediante DTD. Sin embargo, se puede usar DTD en línea para XML con o sin tipo, para suministrar valores predeterminados y reemplazar referencias a entidades por su forma expandida.

Puede convertir las DTD en documentos de esquemas XML mediante herramientas de otros fabricantes y cargar los esquemas XML en la base de datos.

Indizar una columna de tipo de datos xml

Es posible crear índices en columnas de tipo de datos xml. Se indizan todas las etiquetas, los valores y las rutas de acceso de las instancias XML de la columna y se mejora el rendimiento de las consultas. Un índice XML puede afectar positivamente a la aplicación en estas situaciones:

  • Las consultas en columnas XML son habituales en su carga de trabajo. Es preciso considerar el costo de mantenimiento del índice XML durante la modificación de datos.
  • Los valores XML son relativamente grandes y las partes recuperadas son relativamente pequeñas. La generación del índice evita tener que analizar el conjunto de los datos en tiempo de ejecución y favorece las búsquedas basadas en índices que permiten un procesamiento más eficiente de las consultas.

El primer índice de una columna XML es el índice XML principal. Si se usa, se pueden crear tres tipos de índices XML secundarios en la columna XML para acelerar las clases de consultas comunes, como se describe en la siguiente sección.

Índice XML principal

Se indizan todas las etiquetas, los valores y las rutas de acceso de las instancias XML de una columna XML. La tabla base (la tabla en la que aparece la columna XML) debe tener un índice agrupado en la clave principal de la tabla. La clave principal sirve para correlacionar filas de índice con las filas de la tabla base. De las columnas XML, se recuperan instancias XML completas, como por ejemplo SELECT *. Las consultas usan el índice XML principal y devuelven valores escalares o subárboles XML utilizando el propio índice.

Ejemplo: crear un índice XML principal

En la mayoría de los ejemplos, se utiliza la tabla T (pk INT PRIMARY KEY, xCol XML) con una columna XML sin tipo. Se pueden ampliar a XML con tipo de forma directa. Para obtener más información acerca de cómo utilizar XML con tipo, vea Tipo de datos xml. Para simplificar el trabajo, las consultas se describen para instancias de datos XML como se indica a continuación:

<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
   <title>Writing Secure Code</title>
   <author>
      <first-name>Michael</first-name>
      <last-name>Howard</last-name>
   </author>
   <author>
      <first-name>David</first-name>
      <last-name>LeBlanc</last-name>
   </author>
   <price>39.99</price>
</book>

La siguiente instrucción crea un índice XML, llamado idx_xCol, en la columna XML xCol de la tabla T:

CREATE PRIMARY XML INDEX idx_xCol on T (xCol)

Índices XML secundarios

Una vez creado el índice XML principal, es posible que desee crear índices XML secundarios para acelerar distintas clases de consultas dentro de la carga de trabajo. Tres tipos de índices XML secundarios, PATH, PROPERTY y VALUE, favorecen las consultas basadas en rutas de acceso, escenarios personalizados de administración de propiedades y consultas basadas en valores, respectivamente. El índice PATH genera un árbol b+ en el par (ruta de acceso, valor) de cada nodo XML por orden de los documentos en todas las instancias XML de la columna. El índice PROPERTY genera un árbol b+ agrupado en el par (CP, ruta de acceso, valor) dentro de cada instancia XML, donde CP es la clave principal de la tabla base. Por último, el índice VALUE genera un árbol b+ en el par (valor, ruta de acceso) de cada nodo por orden de los documentos en todas las instancias XML de la columna XML.

A continuación, se ofrecen algunas directrices para crear estos índices:

  • Si la carga de trabajo incluye numerosas expresiones de ruta de acceso en las columnas XML, es probable que el índice XML secundario PATH acelere la carga de trabajo. El caso más habitual es el uso del método exist() con columnas XML en la cláusula WHERE de Transact-SQL.
  • Si la carga de trabajo recupera varios valores a partir de instancias XML individuales empleando expresiones de ruta de acceso, puede resultar útil agrupar rutas dentro de cada instancia XML en el índice PROPERTY. Éste suele ser el caso de una bolsa de propiedades, cuando se recuperan las propiedades de un objeto y se conoce el valor de su clave principal.
  • Si la carga de trabajo implica consultar valores dentro de instancias XML sin conocer los nombres de los elementos o atributos que contienen dichos valores, puede ser útil crear el índice VALUE. Esto suele ocurrir con búsquedas en ejes descendentes, como //author[last-name="Howard"], donde los elementos <author> pueden aparecer en cualquier nivel de la jerarquía. También ocurre en consultas con caracteres comodín, como /book [@* = "novel"], donde la consulta busca elementos <book> que tengan algún atributo que contenga el valor "novel".
Ejemplo: búsqueda basada en ruta de acceso

A modo de ilustración, suponga que la consulta siguiente es habitual en su carga de trabajo:

SELECT pk, xCol
FROM   T
WHERE  xCol.exist ('/book/@genre[.="novel"]') = 1

La expresión de ruta de acceso /book/@genre y el valor "novel" corresponden a los campos de clave del índice PATH. Como consecuencia, un índice XML secundario de tipo PATH es útil para esta carga de trabajo:

CREATE XML INDEX idx_xCol_Path on T (xCol)
   USING XML INDEX idx_xCol FOR PATH
Ejemplo: recuperar las propiedades de un objeto

Considere la siguiente consulta, que recupera las propiedades de género, título e ISBN de un libro de cada fila de la tabla T:

SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
    xCol.value ('(/book/title/text())[1]', 'varchar(50)'),
    xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM    T

El índice PROPERTY es útil en este caso y se crea como se indica a continuación:

CREATE XML INDEX idx_xCol_Property on T (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY
Ejemplo: Consulta basada en valores

En la siguiente consulta, el eje descendant-or-self (//) especifica una ruta de acceso parcial, de modo la búsqueda basada en el valor de ISBN se beneficie del uso del índice VALUE:

SELECT xCol
FROM    T
WHERE    xCol.exist ('//book/@ISBN[. = "0-7356-1588-2"]') = 1

El índice VALUE se crea como se indica a continuación:

CREATE XML INDEX idx_xCol_Value on T (xCol)
   USING XML INDEX idx_xCol FOR VALUE

Índice de texto en una columna XML

Puede crear un índice de texto en columnas XML para indizar el contenido de los valores XML, pero omitiendo el marcado XML. El índice de texto no se aplica a los valores de los atributos, puesto que se consideran parte del marcado y se utilizan etiquetas de elementos como límites de testigos. Siempre que sea posible, puede combinar una búsqueda de texto con un índice XML como se indica a continuación:

  • En primer lugar, filtre los valores XML que resulten de interés mediante una búsqueda de texto SQL.
  • A continuación, realice una consulta en los valores XML que usen índice XML en la columna XML.
Ejemplo: combinar una búsqueda de texto con consultas XML

Una vez creado un índice de texto en la columna XML, la siguiente consulta comprueba que un valor XML contiene la palabra "custom" en el título de un libro:

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'custom') 
AND    xCol.exist('/book/title/text()[contains(.,"custom")]') =1

El método contains() utiliza el índice de texto para crear un subconjunto con los valores XML que contienen la palabra "custom" en algún lugar del documento. La cláusula exist() se asegura de que la palabra "custom" aparezca en el título de un libro.

Una búsqueda de texto que usa contains() y la función contains() de XQuery tiene semánticas distintas. La última busca una coincidencia de subcadena y la primera busca una coincidencia de testigo que utilice lematización. Es decir, si se busca la cadena que contiene "run" en el título, las coincidencias incluirán "run", "runs" y "running", puesto que se cumplen los criterios tanto de contains() de texto como los de la función contains() de XQuery. Sin embargo, la consulta no coincide con la palabra "customizable" en el título en que contains() para búsqueda de texto genera un error, pero la sí se cumple la función contains() de Xquery. Por lo general, para una coincidencia de subcadena pura, debe quitarse la cláusula contains() en el caso de una búsqueda de texto.

Además, la búsqueda de texto usa lematización de palabras, pero la función contains() de XQuery busca una coincidencia literal. Esta diferencia se describe en el siguiente ejemplo.

Ejemplo: búsqueda de texto en valores XML mediante lematización

Por lo general, la comprobación de la función contains() de XQuery que se efectuó en el ejemplo anterior no se puede eliminar. Considere esta consulta:

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'run') 

La palabra "ran" en el documento cumple la condición de búsqueda debido a la lematización. Además, el contexto de búsqueda no se comprueba mediante XQuery.

Cuando XML se descompone en columnas relacionales utilizando AXSD con índice de texto, las consultas XPath que se ejecutan en la vista XML no efectúan búsquedas de texto en las tablas subyacentes.

Promoción de propiedades

Si se efectúan consultas principalmente en una cantidad pequeña de valores de elementos y atributos, tal vez desee promover estas cantidades a columnas relacionales. Esto es útil cuando se ejecutan consultas en una pequeña parte de los datos XML mientras se recupera toda la instancia XML. No es necesario crear un índice XML en la columna XML. En lugar de ello, se puede indizar la columna promocionada. Las consultas se deben escribir de modo que usen la columna promocionada. Es decir, que el optimizador de consultas no dirige de nuevo las consultas de la columna XML a la columna promocionada.

La columna promocionada puede ser una columna calculada de la misma tabla o puede ser una columna aparte, mantenida por el usuario, de una tabla. Esto es suficiente cuando se promocionan valores singleton desde cada instancia XML. No obstante, en el caso de propiedades con varios valores, es necesario crear una tabla aparte para la propiedad, como se describe en la sección siguiente.

Columna calculada basada en el tipo de datos xml

Una columna calculada se puede crear mediante una función definida por el usuario que invoque métodos de tipos de datos xml. El tipo de la columna calculada puede ser cualquiera SQL, incluido XML. Esto se muestra en el ejemplo siguiente.

Ejemplo: columna calculada basada en el método de tipo de datos xml

Cree la función definida por el usuario para obtener el número ISBN de un libro:

CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
   DECLARE @ISBN   varchar(20)
   SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
   RETURN @ISBN 
END

Agregue una columna calculada a la tabla para el ISBN:

ALTER TABLE      T
ADD   ISBN AS dbo.udf_get_book_ISBN(xCol)

La columna calculada se puede indizar de la manera habitual.

Ejemplo: consultas en una columna calculada basadas en métodos de tipos de datos xml

Para obtener el <book> cuyo ISBN es 0-7356-1588-2:

SELECT xCol
FROM   T
WHERE  xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1

La consulta en la columna XML se puede volver a escribir de modo que utilice la columna calculada, como se indica a continuación:

SELECT xCol
FROM   T
WHERE  ISBN = '0-7356-1588-2'

Puede crear una función definida por el usuario que devuelva el tipo de datos xml y una columna calculada mediante la función definida por el usuario. Sin embargo, no puede crear un índice XML en la columna XML calculada.

Crear tablas de propiedades

Tal vez desee promocionar algunas de las propiedades con varios valores desde los datos XML a una o más tablas, crear índices en estas tablas y orientar de nuevo las consultas para que las utilicen. Un escenario típico es el caso en que un pequeño número de propiedades cubre casi toda la carga de trabajo de la consulta. Puede hacer lo siguiente:

  • Cree una o más tablas que contengan las propiedades con varios valores. Puede resultarle práctico almacenar una propiedad por tabla y duplicar la clave principal de la tabla base en las tablas de propiedades para combinarlas de nuevo con la tabla base.
  • Si desea mantener el orden relativo de las propiedades, tiene que insertar una columna nueva para el orden relativo.
  • Cree desencadenadores en la columna XML para mantener las tablas de propiedades. En los desencadenadores, realice una de las siguientes operaciones:
    • Use métodos de tipo de datos xml, como nodes() y value(), para insertar y eliminar filas de las tablas de propiedades.
    • Cree funciones de transmisión por secuencias con valores de tabla en Common Language Runtime (CLR) para insertar y eliminar filas de las tablas de propiedades.
    • Escriba consultas para el acceso de SQL a las tablas de propiedades y para el acceso de XML a la columna XML de la tabla base, con combinaciones entre las tablas mediante el uso de su clave principal.
Ejemplo: crear una tabla de propiedades

A modo de ilustración, suponga que desea promocionar el nombre de los autores. Los libros tienen uno o varios autores, por lo que sus nombres son una propiedad con varios valores. Cada nombre se almacena en una fila distinta de una tabla de propiedades. La clave principal de la tabla base se duplica en la tabla de propiedades para que se puedan volver a combinar más tarde.

create table tblPropAuthor (propPK int, propAuthor varchar(max))
Ejemplo: crear una función definida por el usuario para generar un conjunto de filas a partir de una instancia XML

La siguiente función con valores de tabla, udf_XML2Table, acepta un valor de clave principal y una instancia XML. Recupera el nombre de todos los autores de los elementos <book> y devuelve un conjunto de filas de pares de nombres y claves principales.

create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
      select @pk, nref.value('.', 'varchar(max)')
      from   @xCol.nodes('/book/author/first-name') R(nref)
      return
end
Ejemplo: crear desencadenadores para rellenar una tabla de propiedades

El desencadenador insert inserta filas en la tabla de propiedades:

create trigger trg_docs_INS on T for insert
as
      declare @wantedXML xml
      declare @FK int
      select @wantedXML = xCol from inserted
      select @FK = PK from inserted

   insert into tblPropAuthor
   select * from dbo.udf_XML2Table(@FK, @wantedXML)

El desencadenador delete elimina las filas de la tabla de propiedades basándose en el valor de la clave principal de las filas eliminadas:

create trigger trg_docs_DEL on T for delete
as
   declare @FK int
   select @FK = PK from deleted
   delete tblPropAuthor where propPK = @FK

El desencadenador update elimina las filas existentes en la tabla de propiedades correspondientes a la instancia XML actualizada e inserta nuevas filas en la tabla de propiedades:

create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
      declare @FK int
      declare @wantedXML xml
      select @FK = PK from deleted
      delete tblPropAuthor where propPK = @FK

   select @wantedXML = xCol from inserted
   select @FK = pk from inserted

   insert into tblPropAuthor 
      select * from dbo.udf_XML2Table(@FK, @wantedXML)
end
Ejemplo: buscar instancias XML cuyos autores tengan "David" como nombre

La consulta se puede formar en la columna XML. Otra posibilidad es buscar en la tabla de propiedades el nombre "David" y combinarse de nuevo con la tabla base para devolver la instancia XML. Por ejemplo:

SELECT xCol 
FROM     T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE    tblPropAuthor.propAuthor = 'David'
Ejemplo: solución mediante la función de transmisión por secuencias con valores de tabla de CLR

Esta solución consta de los siguientes pasos:

  1. Defina una clase de CLR, SqlReaderBase, que implemente ISqlReader y genere una función de salida de transmisión por secuencias con valores de tabla aplicando una expresión de ruta de acceso en una instancia XML.
  2. Cree un ensamblado y una función Transact-SQL definida por el usuario para iniciar la clase de CLR.
  3. Defina los desencadenadores insert, update y delete mediante la función definida por el usuario para mantener tablas de propiedades.

Para ello, primero deberá crear la función de transmisión por secuencias de CLR. El tipo de datos xml se expone como una clase administrada SqlXml en ADO.NET y admite el método CreateReader() que devuelve un XmlReader.

[!NOTA] El código de ejemplo de esta sección usa XPathDocument y XPathNavigator. De este modo, se fuerza la carga de todos los documentos XML en la memoria. Si utiliza código similar en su aplicación para procesar varios documentos XML grandes, este código no es escalable. En lugar de ello, mantenga asignaciones de memoria de pequeño tamaño y use interfaces de transmisión siempre que sea posible. Para obtener más información acerca del rendimiento, vea Architecture of CLR Integration.

public class c_streaming_xml_tvf {
   public static ISqlReader streaming_xml_tvf 
(SqlXml xmlDoc, string pathExpression) {
      return (new TestSqlReaderBase (xmlDoc, pathExpression));
   }
}

// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;         
   public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;      

   public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {   
      // Variables for XPath navigation
      XPathDocument xDoc;
      XPathNavigator xNav;
      XPathExpression xPath;
   
      // Set sql metadata
      m_rgSqlMetaData = new SqlMetaData[1];
      m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",  
SqlDbType.NVarChar,50);   
   
      //Set up the Navigator
      if (!xmlDoc.IsNull)
          xDoc = new XPathDocument (xmlDoc.CreateReader());
      else
          xDoc = new XPathDocument ();
      xNav = xDoc.CreateNavigator();
      xPath = xNav.Compile (pathExpression);
      m_iterator = xNav.Select(xPath);
   }
   public bool Read() {
      bool moreRows = true;
      if (moreRows = m_iterator.MoveNext())
         FirstName = new SqlChars (m_iterator.Current.Value);
      return moreRows;
   }
}

A continuación, cree un ensamblado y una función Transact-SQL definida por el usuario, SQL_streaming_xml_tvf (no se muestra), que corresponda a la función CLR, streaming_xml_tvf. La función definida por el usuario se utiliza para definir la función con valores de tabla, CLR_udf_XML2Table, para la generación de conjuntos de filas:

create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
   select @pk, FirstName 
   FROM   SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
      return
end

Por último, defina desencadenadores como se muestra en el ejemplo "Crear desencadenadores para rellenar una tabla de propiedades", pero sustituya udf_XML2Table por la función CLR_udf_XML2Table. El desencadenador insert se muestra en el ejemplo siguiente:

create trigger CLR_trg_docs_INS on T for insert
as
   declare @wantedXML xml
   declare @FK int
   select @wantedXML = xCol from inserted
   select @FK = PK from inserted

   insert into tblPropAuthor
      select *
   from    dbo.CLR_udf_XML2Table(@FK, @wantedXML)

El desencadenador delete es idéntico a la versión no CLR. Sin embargo, el desencadenador update simplemente reemplaza la función udf_XML2Table() con CLR_udf_XML2Table().

Colecciones de esquemas XML

Una colección de esquemas XML es una entidad de metadatos que pertenece al ámbito de un esquema relacional. Contiene uno o más esquemas XML que pueden estar relacionados (por ejemplo, a través de <xs:import>) o no. Cada esquema XML de una colección se identifica mediante su espacio de nombres de destino.

Una colección de esquemas XML se crea mediante la sintaxis CREATE XML SCHEMA COLLECTION (Transact-SQL) e indicando uno o más esquemas XML. Es posible agregar componentes de esquema XML adicionales a un esquema XML existente, y más esquemas a una colección de esquemas XML mediante la sintaxis ALTER XML SCHEMA COLLECTION (Transact-SQL). Las colecciones de esquemas XML se pueden proteger igual que otros objetos SQL mediante el modelo de seguridad de SQL Server 2005.

Columna con varios tipos

Una colección de esquemas XML C proporciona tipos a una columna XML, xCol, de acuerdo con varios esquemas XML. Además, los indicadores DOCUMENT y CONTENT especifican si se pueden almacenar árboles o fragmentos XML, respectivamente, en la columna xCol.

En el caso de DOCUMENT, cada instancia XML especifica el espacio de nombres de destino de su elemento de nivel superior en la instancia, al que se proporciona un tipo y se valida según esto. Por otra parte, en el caso de CONTENT, cada elemento de nivel superior puede especificar cualquiera de los espacios de nombres de destino en C. La instancia XML se valida y recibe un tipo según todos los espacios de nombres de destino que aparecen en una instancia.

Evolución de esquemas

Una colección de esquemas XML sirve para proporcionar un tipo a columnas, variables y parámetros XML. Ofrece un mecanismo para la evolución de esquemas XML. A modo de ilustración, suponga que agrega un esquema XML con el espacio de nombres de destino BOOK-V1 a una colección de esquemas XML C. Una columna XML, xCol que recibe un tipo a partir de C, puede almacenar datos XML que se ajustan al esquema de BOOK-V1.

A continuación, suponga que una aplicación desea ampliar el esquema XML con nuevos componentes, como definiciones de tipo complejas y declaraciones de elementos de nivel superior. Estos nuevos componentes de esquema se pueden agregar al esquema BOOK-V1 y no requieren una nueva validación de los datos XML existentes en la columna xCol.

Suponga que, más tarde, la aplicación desea proporcionar una nueva versión del esquema XML y selecciona el espacio de nombres de destino BOOK-V2. Este esquema XML se puede agregar a C. La columna XML puede almacenar instancias de BOOK-V1 y BOOK-V2, y ejecutar consultas y modificaciones de datos en instancias XML que se ajustan a estos espacios de nombres.

Cargar datos XML

Transferir datos XML de SQL Server 2000 a SQL Server 2005

Existen varios procedimientos para transferir datos XML a SQL Server 2005. Por ejemplo:

  • Si tiene sus datos en una columna [n]text o image en una base de datos de SQL Server 2000, puede importar la tabla a una base de datos de SQL Server 2005 mediante SQL Server 2005 Integration Services (SSIS). Cambie el tipo de columna a XML mediante la instrucción ALTER TABLE.
  • Puede realizar una copia masiva de los datos desde SQL Server 2000 mediante bcp out y, después, hacer una inserción masiva de los datos en la base de datos de SQL Server 2005 mediante bcp in.
  • Si tiene datos en columnas relacionales de una base de datos de SQL Server 2000, cree una tabla nueva con una columna [n]text y, si lo desea, una columna de clave principal para disponer de un identificador de fila. Use programación de cliente para recuperar el XML que se genera en el servidor con FOR XML y escribirlo en la columna [n]text. A continuación, use las técnicas mencionadas previamente para transferir datos a una base de datos de SQL Server 2005. Puede optar por escribir el XML en una columna XML en la base de datos de SQL Server 2005 directamente.
Ejemplo: cambiar el tipo de columna a XML

Suponga que desea cambiar el tipo de una columna [n]text o image, XYZ en la tabla R, a XML sin tipo. La siguiente instrucción realiza este cambio de tipo:

ALTER TABLE R ALTER COLUMN XYZ XML
  • El destino puede ser de tipo XML, si es necesario, especificando una colección de esquemas XML.

Carga masiva de datos XML

Puede realizar una carga masiva de datos XML en el servidor mediante las funciones de carga masiva de SQL Server, como bcp. OPENROWSET permite cargar datos en una columna XML desde archivos. El siguiente ejemplo muestra esta función.

Ejemplo: cargar XML desde archivos

Este ejemplo muestra cómo insertar una fila en la tabla T. El valor de la columna XML se carga desde el archivo C:\MyFile\xmlfile.xml como CLOB y se suministra el valor 10 a la columna de enteros.

INSERT INTO T
SELECT 10, xCol
FROM    (SELECT *    
    FROM OPENROWSET (BULK 'C:\MyFile\xmlfile.xml', SINGLE_CLOB) 
 AS xCol) AS R(xCol)

Codificación de texto

SQL Server 2005 almacena los datos XML en Unicode (UTF-16). Los datos XML recuperados del servidor se reciben con codificación UTF-16. Si desea otra codificación, tendrá que realizar la conversión necesaria en los datos recuperados. En ocasiones, los datos XML pueden tener una codificación distinta. Si éste es el caso, debe prestar atención al cargar los datos. Por ejemplo:

  • Si el XML de texto es Unicode (UCS-2, UTF-16), puede asignarlo a una columna, una variable o un parámetro XML sin problemas.
  • Si la codificación no es Unicode y está implícita, debido a la página de códigos original, la página de códigos de cadena de la base de datos debe coincidir o ser compatible con los puntos de código que desea cargar. Si es necesario, use COLLATE. Si no existe tal página de códigos de servidor, deberá agregar una declaración XML explícita con la codificación correcta.
  • Para usar una codificación explícita, utilice el tipo varbinary(), que no tiene interacción con páginas de códigos, o un tipo de cadena de la página de códigos apropiada. A continuación, asigne los datos a una columna, una variable o un parámetro XML.
Ejemplo: especificar explícitamente una codificación

Suponga que tiene un documento XML, vcdoc, almacenado como varchar(max), que no dispone de una declaración XML explícita. La instrucción siguiente agrega una declaración XML con la codificación "iso8859-1", concatena el documento XML, convierte el resultado a varbinary(max) de modo que se preserve la representación de bytes y, finalmente, lo convierte a XML. De este modo, se habilita el procesador XML para analizar los datos según la codificación especificada "iso8859-1" y generar la representación UTF-16 correspondiente para los valores de cadena.

SELECT CAST( 
CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc) AS VARBINARY (MAX)) 
 AS XML)

XQuery e inferencia de tipos

XQuery incrustado en Transact-SQL es el lenguaje admitido para realizar consultas de tipos de datos xml. El lenguaje está siendo desarrollado por el World Wide Web Consortium (W3C), con la participación de los principales proveedores de bases de datos de Microsoft. Incluye XPath versión 2.0 como lenguaje de exploración. En el tipo de datos xml también hay construcciones de lenguaje disponibles para modificar los datos. Para obtener más información acerca de las construcciones, las funciones y los operadores de XQuery admitidos en SQL Server, vea Funciones de XQuery con el tipo de datos xml.

Modelo de error

Las expresiones XQuery e instrucciones XML DML sintácticamente incorrectas generan errores de compilación. En la fase de compilación, se comprueba la corrección del tipo estático de las expresiones XQuery y las instrucciones DML, y se utilizan esquemas XML para detectar inferencias de tipos para XML con tipo. Si una expresión puede generar un error en tiempo de ejecución debido a una infracción de seguridad de tipos, se producen errores de tipo estático. Ejemplos de errores estáticos son la suma de una cadena a un entero y la consulta de datos con tipo en un nodo no existente

Como desviación del estándar W3C, los errores en tiempo de ejecución de XQuery se convierten en secuencias vacías. Estas secuencias se pueden propagar al resultado de la consulta como XML vacío o NULL, dependiendo del contexto de invocación.

Una conversión explícita al tipo correcto permite a los usuarios resolver errores estáticos, aunque los errores de conversión en tiempo de ejecución se transformarán en secuencias vacías.

En las secciones siguientes se describe la comprobación de tipos con más detalle.

Comprobaciones de singleton

Los pasos de ubicación, los parámetros de funciones y los operadores que requieren singleton devolverán un error si el compilador no puede determinar si se garantiza un singleton en tiempo de ejecución. Este problema es frecuente con datos sin tipo. Por ejemplo, la búsqueda de un atributo requiere un elemento primario singleton. Es suficiente con un ordinal que seleccione un solo nodo primario. Es posible que la evaluación de una combinación node()-value() para extraer valores de atributos no requiera la especificación del ordinal. Esto se muestra en el ejemplo siguiente.

Ejemplo: singleton conocido

En este ejemplo, el método nodes() genera una fila distinta para cada elemento <book>. El método value() que se evalúa en un nodo <book> extrae el valor de @genre y, siendo un atributo, es un singleton.

SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   T CROSS APPLY xCol.nodes('//book') AS R(nref)

El esquema XML se utiliza para comprobar el tipo del XML con tipo. Si se especifica un nodo como singleton en el esquema XML, el compilador usa esa información y no se produce ningún error. En caso contrario, se necesita un ordinal que seleccione un solo nodo. En particular, el uso de eje descendant-or-self (//), como en /book//title, pierde inferencia de cardinalidad de singleton para el elemento <title>, incluso si el esquema XML especifica que sea así. Por tanto, se debe volver a escribir como (/book//title)[1].

Es importante ser consciente de la diferencia entre //first-name[1] y (//first-name)[1] para la comprobación de tipos. La primera expresión devuelve una secuencia de nodos <first-name> en la que cada nodo es el que está más a la izquierda entre los de su mismo nivel. La última expresión devuelve el primer nodo singleton <first-name> por orden de los documentos en la instancia XML.

Ejemplo: usar value()

La siguiente consulta en una columna XML sin tipo da como resultado un error de compilación estático. Esto se debe a que value() espera un nodo singleton como primer argumento y el compilador no puede determinar si sólo va a aparecer un nodo <last-name> en tiempo de ejecución:

SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM   T

A continuación, se ofrece una solución que debe contemplar:

SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM   T

No obstante, esta solución no resuelve el error, ya que pueden aparecer varios nodos <author> en cada instancia XML. Resulta útil volver a escribir lo siguiente:

SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T

Esta consulta devuelve el valor del primer elemento <last-name> de cada instancia XML.

Eje primario

Si no se puede determinar el tipo de un nodo, adopta anyType. Este tipo no se convierte implícitamente en ningún otro tipo. Esto ocurre especialmente durante las exploraciones que utilizan el eje primario; por ejemplo, xCol.query('/book/@genre/../price'). Se determina que el tipo de nodo primario sea anyType. Un elemento también se puede definir como anyType en un esquema XML. En ambos casos, la pérdida de información de tipo más precisa frecuentemente da lugar a errores de tipo estático y requiere una conversión explícita de valores atómicos en su tipo específico.

Descriptores de acceso data(), text() y string()

XQuery tiene una función fn:data() para extraer valores escalares con tipo a partir de nodos, una prueba de nodo text() para devolver nodos de texto, y la función fn:string() que devuelve el valor de cadena de un nodo. Su uso puede resultar confuso. A continuación, se presentan las directrices para usarlas correctamente en SQL Server 2005. A modo de ilustración, se utiliza la instancia XML <age>12</age>.

  • XML sin tipo: la expresión de ruta de acceso /age/text() devuelve el nodo de texto "12". La función fn:data(/age) devuelve el valor de cadena "12", igual que fn:string(/age).
  • XML con tipo: la expresión /age/text() devuelve un error estático para cada elemento <age> simple con tipo. Por otra parte, fn:data(/age) devuelve el entero 12. fn:string(/age) da como resultado la cadena "12".

Funciones y operadores sobre tipos de unión

Los tipos de unión requieren un tratamiento especial debido a la comprobación de tipos. Dos de los problemas se muestran en los ejemplos siguientes.

Ejemplo: función sobre tipo de unión

Considere la definición de un elemento para <r> de un tipo de unión:

<xs:element name="r">
<xs:simpleType>
   <xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>

En el contexto de XQuery, la función "promedio" fn:avg (//r) devuelve un error estático, porque el compilador de XQuery no puede agregar valores de distintos tipos (xs:int, xs:float o xs:double) para los elementos <r> en el argumento de fn:avg(). Para resolverlo, vuelva a escribir la invocación de la función como fn:avg(for $r in //r return $r cast as xs:double ?).

Ejemplo: operador sobre tipo de unión

La operación de suma ("+") requiere tipos precisos para los operandos. Como resultado, la expresión (//r)[1] + 1 devuelve un error estático que tiene la definición de tipo descrita previamente para el elemento <r>. Una solución es volver a escribirla como (//r)[1] cast as xs:int? +1, donde "?" indica 0 ó 1 repeticiones. SQL Server 2005 requiere "cast as" con "?", porque cualquier conversión puede provocar la secuencia vacía como resultado de errores en tiempo de ejecución.

value(), nodes() y OpenXML()

Puede usar varios métodos value() en el tipo de datos xml de una cláusula SELECT para generar un conjunto de filas de valores extraídos. El método nodes() da como resultado una referencia interna para cada nodo seleccionado, que se puede usar para hacer más consultas. La combinación de los métodos nodes() y value() puede ser más eficaz para generar el conjunto de filas cuando tiene varias columnas y, tal vez, cuando las expresiones de ruta de acceso empleadas en su generación son complejas.

El método nodes() da como resultado instancias de un tipo de datos xml especial, cada una de las cuales tiene su contexto establecido en un nodo seleccionado diferente. Este tipo de instancia XML admite los métodos query(), value(), nodes() y exist() y se puede usar en agregaciones count(*). Otros usos generarían un error.

Ejemplo: usar nodes()

Suponga que desea extraer el nombre y los apellidos de los autores cuyo nombre no sea "David". Además, desea extraer esta información como un conjunto de filas que contiene dos columnas: FirstName y LastName. Con los métodos nodes() y value() puede lograrlo, como se indica a continuación:

SELECT nref.value('(first-name/text())[1]', 'nvarchar(50)') FirstName,
       nref.value('(last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('first-name[. != "David"]') = 1

En este ejemplo, nodes('//author') da como resultado un conjunto de filas de referencias a elementos <author> para cada instancia XML. Los nombres y apellidos de los autores se obtienen evaluando los métodos value() relativos a esas referencias.

SQL Server 2000 permite generar un conjunto de filas a partir de una instancia XML mediante OpenXml(). Puede especificar el esquema relacional para el conjunto de filas y la manera en que se asignan los valores dentro de la instancia XML a columnas del conjunto de filas.

Ejemplo: usar OpenXml() en el tipo de datos xml

La consulta se puede volver a escribir a partir del ejemplo anterior utilizando OpenXml() como se indica a continuación. Para ello, se crea un cursor que lee cada instancia XML en una variable XML y luego le aplica OpenXML:

DECLARE name_cursor CURSOR
FOR
   SELECT xCol 
   FROM   T
OPEN name_cursor
DECLARE @xmlVal XML
DECLARE @idoc int
FETCH NEXT FROM name_cursor INTO @xmlVal

WHILE (@@FETCH_STATUS = 0)
BEGIN
   EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal
   SELECT   *
   FROM   OPENXML (@idoc, '//author')
          WITH (FirstName  varchar(50) 'first-name',
                LastName   varchar(50) 'last-name') R
   WHERE  R.FirstName != 'David'

   EXEC sp_xml_removedocument @idoc
   FETCH NEXT FROM name_cursor INTO @xmlVal
END
CLOSE name_cursor
DEALLOCATE name_cursor 

OpenXml() crea una representación en la memoria y utiliza tablas de trabajo en lugar del procesador de consultas. Se basa en el procesador de XPath versión 1.0 de MSXML versión 3.0 en lugar del motor de XQuery. Las tablas de trabajo no se comparten entre varias llamadas a OpenXml(), ni siquiera en la misma instancia XML. Esto limita su escalabilidad. OpenXml() permite el acceso a un formato de tabla irregular para los datos XML cuando no se especifica la cláusula WITH. Además, permite utilizar el valor XML restante en una columna de "desbordamiento" independiente.

La combinación de las funciones nodes() y value() usa índices XML con eficacia. Como resultado, esta combinación puede ofrecer una mayor escalabilidad que OpenXml.

Usar FOR XML para generar XML a partir de conjuntos de filas

Se puede generar una instancia de tipo de datos xml a partir de un conjunto de filas utilizando para ello FOR XML con la nueva directiva TYPE.

El resultado se puede asignar a una columna, una variable o un parámetro de tipo de datos xml. Además, FOR XML se puede anidar para generar una estructura jerárquica. Por ello, FOR XML anidado resulta mucho más cómodo de escribir que FOR XML EXPLICIT, aunque tal vez no funcione tan bien para jerarquías con muchos niveles. FOR XML también incorpora un nuevo modo PATH. Este nuevo modo especifica la ruta de acceso en el árbol XML donde aparece un valor de columna.

La nueva directiva FOR XML TYPE puede emplearse para definir vistas XML de sólo lectura en datos relacionales con sintaxis SQL. Es posible realizar consultas en la vista con instrucciones SQL y XQuery incrustado, como se indica en el ejemplo siguiente. También se puede hacer referencia a estas vistas SQL en procedimientos almacenados.

Ejemplo: vista SQL que devuelve el tipo de datos xml generado

La siguiente definición de vista SQL crea una vista XML de una columna relacional, pk, y de los autores de libros recuperados de una columna XML:

CREATE VIEW V (xmlVal) AS
SELECT pk, xCol.query('/book/author')
FROM   T
FOR XML AUTO, TYPE

La vista V contiene una sola fila y una sola columna xmlVal de tipo XML. Se puede consultar como una instancia de tipo de datos xml normal. Por ejemplo, la siguiente consulta devuelve el autor cuyo nombre es "David":

SELECT xmlVal.query('//author[first-name = "David"]')
FROM   V

Las definiciones de vistas SQL son en cierto modo similares a las vistas XML que se crean mediante esquemas anotados. Sin embargo, hay tres diferencias importantes. La definición de vista SQL es de sólo lectura y se debe manipular con XQuery incrustado. Las vistas XML se crean mediante un esquema anotado. Además, la vista SQL materializa el resultado XML antes de aplicar la expresión XQuery, mientras que las consultas XPath en las vistas XML evalúan consultas SQL en las tablas subyacentes.

Agregar lógica de negocios

La lógica de negocios se puede agregar a los datos XML de varias formas:

  • Se pueden escribir restricciones de filas o columnas para forzar restricciones específicas de un dominio durante la inserción y modificación de datos XML.
  • Es posible escribir un desencadenador en la columna XML que se active al insertar o actualizar valores en la columna. El desencadenador puede contener reglas de validación específicas de un dominio o rellenar tablas de propiedades.
  • Se pueden escribir funciones SQLCLR en código administrado a las que se pasen valores XML y usar las funciones de procesamiento de XML que proporciona el espacio de nombres System.Xml. Un ejemplo es aplicar la transformación XSL a datos XML. Otra posibilidad es deserializar el XML en una o más clases administradas y operar con ellas mediante código administrado.
  • Se pueden escribir procedimientos almacenados de Transact-SQL y funciones que inicien el procesamiento en la columna XML de acuerdo con las necesidades de la empresa.
Ejemplo: aplicar una transformación XSL

Considere una función CLR TransformXml() que acepta una instancia de tipo de datos xml y una transformación XSL almacenada en un archivo, aplica la transformación a los datos XML y, a continuación, devuelve el XML transformado en el resultado. A continuación, se muestra una función esquemática escrita en C#:

public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslCompiledTransform xform = new XslCompiledTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load(xslDoc);

   // Load XML data 
   XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());

   // Return the transformed value
   MemoryStream xsltResult = new MemoryStream();
   xform.Transform(xDoc, null, xsltResult);
   SqlXml retSqlXml = new SqlXml(xsltResult);
   return (retSqlXml);
} 

Después de registrar el ensamblado y crear una función Transact-SQL definida por el usuario, SqlXslTransform() correspondiente a TransformXml(), se puede invocar la función desde Transact-SQL como se indica en la consulta siguiente:

SELECT SqlXslTransform (xCol, 'C:\MyFile\xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

El resultado de la consulta contiene un conjunto de filas del XML transformado.

SQLCLR amplía las posibilidades de descomposición de datos XML en tablas o promoción de propiedades, y de consulta de datos XML utilizando clases administradas en el espacio de nombres System.Xml. Para obtener más información, vea los Libros en pantalla de SQL Server y la documentación del SDK de .NET Framework.

Consultas entre dominios

Si los datos residen en una combinación de columnas de tipo de datos xml y relacionales, tal vez desee escribir consultas que combinen procesamiento de datos XML y relacionales. Por ejemplo, puede convertir los datos de columnas XML y relacionales en una instancia de tipo de datos xml utilizando FOR XML y realizando una consulta mediante XQuery. En contraposición, puede generar un conjunto de filas a partir de valores XML y realizar una consulta mediante Transact-SQL.

Un modo más práctico y eficaz de escribir consultas entre dominios es usar el valor de una variable o columna SQL dentro de expresiones XQuery o XML DML:

  • Puede emplear sql:variable() para usar el valor de una variable SQL en la expresión XQuery o XML DML.
  • Puede emplear sql:column() para usar los valores de una columna de relación en la expresión XQuery o XML DML.

Estos dos enfoques permiten a las aplicaciones parametrizar consultas, como se indica en el siguiente ejemplo. Sin embargo, los tipos XML y los definidos por el usuario no se permiten en sql:variable() y sql:column().

Ejemplo: consulta entre dominios mediante sql:variable()

La siguiente consulta es una versión modificada de la que aparece en "Ejemplo: consultas en una columna calculada basadas en métodos de tipos de datos xml". En la versión siguiente, este ISBN concreto se pasa mediante una variable SQL @isbn. Sustituyendo la constante por sql:variable(), la consulta sirve para buscar cualquier ISBN y no sólo el que corresponde a 0-7356-1588-2.

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
SELECT  xCol
FROM    T
WHERE   xCol.exist ('/book/@ISBN[. = sql:variable("@isbn")]') = 1

sql:column() se puede utilizar de manera similar y ofrece otras ventajas. Los índices en la columna favorecen la eficiencia, según se decida mediante el optimizador de consultas basado en costos. Además, la columna calculada puede almacenar una propiedad promocionada.

Vistas de catálogo para la compatibilidad con XML nativo

Las vistas de catálogo sirven para proporcionar información de metadatos acerca del uso del XML. Algunas se describen en la sección siguiente.

Índices XML

Las entradas de índice XML aparecen en la vista de catálogo sys.indexes, con el "tipo" de índice 3. La columna de nombres contiene el nombre del índice XML.

Los índices XML también se registran en la vista de catálogo sys.xml_indexes. Ésta contiene todas las columnas de sys.indexes y algunas específicas que son útiles para índices XML. El valor NULL de la columna secondary_type indica un índice XML principal; los valores "P", "R" y "V" representan los índices XML secundarios PATH, PROPERTY y VALUE respectivamente.

Es posible encontrar el uso de espacio de los índices XML en la función con valores de tabla sys.dm_db_index_physical_stats. Contiene información como el número de páginas de disco ocupadas, el tamaño medio de las filas en bytes y el número de registros de todos los tipos de índice. Se refiere también a los índices XML. Esta información está disponible para cada partición de base de datos. Los índices XML usan el mismo esquema de partición y la misma función de partición que la tabla base.

Recuperar colecciones de esquemas XML

Las colecciones de esquemas XML se enumeran en la vista de catálogo sys.xml_schema_collections. La colección de esquemas XML "sys" la define el sistema. Contiene los espacios de nombres predefinidos que se pueden usar en todas las colecciones de esquemas XML definidas por el usuario, sin tener que cargarlos explícitamente. Esta lista contiene los espacios de nombres para xml, xs, xsi, fn y xdt. Otras dos vistas de catálogo son sys.xml_schema_namespaces, que enumera todos los espacios de nombres incluidos en una colección de esquemas XML, y sys.xml_components, que enumera todos los componentes de esquemas XML dentro de cada esquema XML.

La función integrada XML_SCHEMA_NAMESPACE, schemaName, XmlSchemacollectionName, namespace-uri, genera una instancia de tipo de datos xml. Esta instancia contiene fragmentos de esquemas XML correspondientes a esquemas incluidos en una colección de esquemas XML, excepto los esquemas XML predefinidos.

Puede enumerar el contenido de una colección de esquemas XML de las siguientes maneras:

  • Escriba consultas Transact-SQL sobre las vistas de catálogo apropiadas para colecciones de esquemas XML.
  • Use la función integrada XML_SCHEMA_NAMESPACE(). Puede aplicar métodos de tipo de datos xml al resultado de esta función. Sin embargo, no puede modificar los esquemas XML subyacentes.

Los ejemplos siguientes ilustran lo comentado.

Ejemplo: enumerar los espacios de nombres XML en una colección de esquemas XML

Use la siguiente consulta para la colección de esquemas XML "myCollection".

SELECT XSN.name
FROM    sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN
    ON (XSC.xml_collection_id = XSN.xml_collection_id)
WHERE    XSC.name = 'myCollection'   
Ejemplo: enumerar el contenido de una colección de esquemas XML

La instrucción siguiente enumera el contenido de la colección de esquemas XML "myCollection" dentro del esquema relacional dbo.

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection')

Es posible obtener esquemas XML individuales incluidos en la colección como instancias de tipo de datos xml; para ello, especifique el espacio de nombres de destino como tercer argumento de XML_SCHEMA_NAMESPACE(). Esto se muestra en el ejemplo siguiente.

Ejemplo: obtener un esquema especificado a partir de una colección de esquemas XML

La instrucción siguiente genera como resultado el esquema XML con el espacio de nombres de destino "https://www.microsoft.com/books" a partir de la colección de esquemas XML "myCollection" dentro del esquema relacional dbo.

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'https://www.microsoft.com/books')

Consultar esquemas XML

Los siguientes procedimientos permiten consultar esquemas XML que se hayan cargado en colecciones de esquemas XML:

  • Escriba consultas Transact-SQL sobre vistas de catálogo para espacios de nombres de esquemas XML.
  • Cree una tabla que contenga una columna de tipo de datos xml para almacenar los esquemas XML y también cargarlos en el sistema de tipo XML. Puede consultar la columna XML mediante los métodos de tipo de datos xml. Además, puede crear un índice XML en esta columna. Sin embargo, con este enfoque, la aplicación debe mantener la coherencia entre los esquemas XML almacenados en la columna XML y el sistema de tipo XML. Por ejemplo, si se quita el espacio de nombres de esquemas XML del sistema de tipo XML, también se tiene que quitar de la tabla para preservar la coherencia.

Vea también

Referencia

Administrar colecciones de esquemas XML en el servidor
Funciones de XQuery con el tipo de datos xml

Conceptos

Tipo de datos xml

Otros recursos

sys.dm_db_index_physical_stats
Introducción a la búsqueda de texto

Ayuda e información

Obtener ayuda sobre SQL Server 2005