Méthodes conseillées en XML

SQL Server 2005 fournit une prise en charge étendue du traitement des données XML. Les valeurs XML peuvent être stockées de façon native dans une colonne de type de données xml qui peut être typée en fonction d'une collection de schémas XML ou rester non typée. Vous pouvez indexer la colonne XML. De plus, une manipulation très fine des données est rendue possible grâce à XQuery et à XML DML. XML DML est une extension qui facilite la modification des données.

SQL Server 2000 et les versions Web SQLXML offrent des fonctions très puissantes de gestion des données XML. Ces fonctionnalités s'attachent à mettre en corrélation les données relationnelles et les données XML. Des vues XML de données relationnelles peuvent être définies à l'aide du langage AXSD (XSD annoté) pour fournir une approche véritablement XML prenant en charge les fonctionnalités de chargement en bloc, d'interrogation et de mise à jour des données XML. De leur côté, les extensions Transact-SQL fournissent une approche centrée sur SQL pour mapper les résultats des requêtes relationnelles à des données XML grâce à la clause FOR XML, et pour créer des vues relationnelles à partir de données XML à l'aide de OPENXML. Ces prises en charge ont été étendues dans SQL Server 2005. Avec la nouvelle prise en charge de XML natif, SQL Server 2005 constitue une puissante plateforme de développement d'applications d'une grande richesse pour la gestion des données semi-structurées et structurées.

Cette rubrique explique les grandes lignes de la modélisation et de l'utilisation des données XML dans SQL Server 2005. Elle se divise en deux sections :

  • Modélisation des données
    Les données XML peuvent être stockées de plusieurs manières dans SQL Server 2005 : soit en utilisant le type de données xml natif, soit en fragmentant le code XML dans les tables. Cette rubrique vous guide dans le choix de la technique de modélisation la plus adaptée à vos données XML. Elle aborde aussi l'indexation des données XML, la promotion des propriétés et le typage des instances XML.
  • Utilisation des données
    Cette section aborde tous les thèmes relatifs à l'utilisation des données, notamment le chargement des données XML dans le serveur et l'inférence de type lors de la compilation des requêtes. Cette section explique aussi les fonctionnalités apparentées et les différencie afin de vous suggérer la meilleure façon de les utiliser. Toutes ces suggestions sont illustrées d'exemples.

Modélisation des données

Cette section expose les raisons pour lesquelles vous devez utiliser XML dans SQL Server 2005. Elle vous aide aussi à évaluer les avantages et les inconvénients du stockage XML natif et des vues XML, et vous propose des suggestions quant à la modélisation des données.

Modèle de données relationnel ou XML

Si vos données sont très structurées et s'accompagnent de schémas connus, le modèle relationnel est sans doute le mieux adapté au stockage des données. SQL Server fournit les fonctionnalités et les outils dont vous pourrez avoir besoin. En revanche, s'il s'agit de données semi-structurées ou non structurées, ou si la structure est inconnue, mieux vaut envisager la modélisation de ces données.

XML s'avère une solution de choix si vous souhaitez un modèle indépendant des plateformes pour assurer la portabilité des données à l'aide d'un balisage structurel et sémantique. De plus, cette solution semble mieux adaptée si certaines des conditions suivantes sont réunies :

  • Vos données sont fragmentées, vous n'en connaissez pas la structure, ou la structure de vos données risque d'évoluer considérablement dans l'avenir.
  • Vos données représentent une hiérarchie d'imbrications, et non des références entre entités, et peuvent être récursives.
  • L'ordre est inhérent dans vos données.
  • Vous souhaitez interroger les données ou en mettre à jour certaines en fonction de leur structure.

Si aucune de ces conditions n'est remplie, il est préférable d'utiliser le modèle de données relationnel. Par exemple, si vos données sont au format XML, alors que votre application n'utilise la base de données que pour stocker et récupérer les données, une colonne [n]varchar(max) suffit. Le stockage des données dans une colonne XML apporte d'autres avantages : le moteur peut vérifier que les données sont bien formées ou valides, et vous pouvez lancer des requêtes et des mises à jour à granularité fine sur les données XML.

Raisons justifiant le stockage des données XML dans SQL Server 2005

Vous trouverez ici quelques bonnes raisons d'opter pour les fonctions XML natives de SQL Server 2005 au lieu de gérer vos données XML dans le système de fichiers :

  • Vous voulez partager, interroger et modifier vos données XML d'une manière efficace et transactionnelle. Votre application a besoin d'accéder aux données à un niveau très détaillé. Par exemple, vous souhaitez extraire quelques passages d'un document XML, ou insérer une nouvelle section, sans avoir à remplacer l'ensemble du document.
  • Vous disposez de données relationnelles et de données XML et souhaitez garantir une parfaite interopérabilité entre elles au sein de votre application.
  • Vous cherchez un langage permettant d'interroger et de modifier les données d'applications inter-domaines.
  • Vous souhaitez que le serveur vérifie la bonne formation des données et valide éventuellement vos données par rapport à des schémas XML.
  • Vous cherchez à indexer les données XML pour optimiser le traitement des requêtes et faciliter la montée en charge, et voulez profiter d'un optimiseur de requête de tout premier ordre.
  • Vous voulez pouvoir accéder aux données XML par le biais de SOAP, ADO.NET et OLE DB.
  • Vous voulez profiter des fonctions d'administration du serveur de base de données pour gérer vos données XML, en cas de sauvegarde, de restauration et de réplication par exemple.

Si aucune de ces conditions n'est remplie, vous avez intérêt à stocker vos données dans un type d'objet volumineux non-XML, tel que [n]varchar(max) ou varbinary(max).

Options de stockage XML

Dans SQL Server 2005, vous disposez des options de stockage suivantes :

  • Stockage en mode natif dans le type de données xml
    Les données sont stockées dans une représentation interne qui conserve le contenu XML des données. Il peut s'agir d'une hiérarchie d'imbrications, de l'ordre d'un document ou de valeurs d'éléments et d'attributs. Plus précisément, le contenu InfoSet des données XML est préservé. Pour plus d'informations sur InfoSet, consultez http://www.w3.org/TR/xml-infoset. Le contenu InfoSet n'est pas une copie conforme du texte XML puisque les éléments suivants ne sont pas conservés : espaces non significatifs, ordre des attributs, préfixes d'espace de noms et déclaration XML.
    Pour un type de données xml typé, un type de données xml lié à des schémas XML, le jeu d'informations de validation post-schéma (PSVI) ajoute des informations de type dans le jeu d'informations et il est codé dans la représentation interne. L'analyse s'en trouve considérablement accélérée. Pour plus d'informations, consultez les spécifications des schémas XML du W3C à l'adresse http://www.w3.org/TR/xmlschema-1 et à l'adresse http://www.w3.org/TR/xmlschema-2.
  • Mappage entre stockage XML et relationnel
    À l'aide d'un schéma annoté (AXSD), le code XML est décomposé en colonnes dans une ou plusieurs tables de façon à préserver la fidélité des données au niveau relationnel. La structure hiérarchique est ainsi conservée bien que l'ordre des éléments soit ignoré. Le schéma ne peut pas être récursif.
  • Stockage d'objets volumineux, [n]varchar(max) et varbinary(max)
    Une copie conforme des données est stockée. Cela s'avère nécessaire pour des applications spécifiques telles que des documents juridiques. Généralement, les applications ne réclament pas une copie conforme et se satisfont du contenu XML (fidélité de l'InfoSet).

Dans la plupart des cas, vous aurez probablement à combiner ces deux approches. Vous pouvez, par exemple, stocker vos données XML dans une colonne de type de données xml et en promouvoir les propriétés dans des colonnes relationnelles. Vous pouvez également mapper les technologies de façon à stocker uniquement les parties récursives dans des colonnes de type de données xml et les parties non récursives dans des colonnes non-XML.

Choix de la technologie XML

Le choix de la technologie XML, mode XML natif ou vue XML, dépend généralement des facteurs suivants :

  • Option de stockage
    Vos données XML sont peut-être plus adaptées à un stockage d'objet volumineux (manuel de produit, par exemple) ou à un stockage en colonnes relationnelles (article converti en XML, par exemple). Chaque option de stockage préserve la fidélité du document à sa manière.
  • Interrogation des données par requête
    Vous trouverez peut-être qu'une option de stockage convient mieux qu'une autre en fonction de la nature de vos requêtes et de la façon dont vous interrogez vos données XML. Les requêtes à granularité fine sur des données XML, évaluation de prédicat sur des nœuds XML par exemple, ne sont prises en charge qu'à des degrés divers dans les deux options de stockage.
  • Indexation des données XML
    Vous cherchez peut-être à indexer les données XML de manière à optimiser les performances des requêtes XML. Les options d'indexation varient avec les options de stockage. Il vous appartient de choisir la solution la mieux adaptée à l'optimisation de votre charge de travail.
  • Modification des données
    Certaines charges de travail impliquent la modification des données XML à un degré de granularité assez fin. Il peut s'agir, notamment, d'ajouter une nouvelle section à un document sans que d'autres charges de travail, le contenu Web par exemple, ne soient concernées. La prise en charge d'un langage de modification des données peut jouer un rôle essentiel pour votre application.
  • Prise en charge des schémas
    Vos données XML peuvent être décrites par un schéma qui peut (ou non) être un document de schéma XML. La prise en charge de code XML associé à un schéma dépend de la technologie XML adoptée.

Qui dit choix différents, dit aussi performances différentes.

Stockage XML natif

Vous pouvez stocker vos données XML dans une colonne de type de données xml sur le serveur. C'est une solution de choix si vous vous trouvez dans les conditions suivantes :

  • Vous cherchez un moyen simple de stocker vos données XML sur le serveur et voulez, en parallèle, préserver l'ordre et la structure du document.
  • Vous disposez ou non d'un schéma pour vos données XML.
  • Vous voulez pouvoir interroger et modifier vos données XML.
  • Vous voulez indexer les données XML pour accélérer le traitement des requêtes.
  • Votre application a besoin d'affichages catalogue système pour gérer vos données XML et vos schémas XML.

Le stockage XML natif est utile lorsque les documents XML présentent des structures différentes ou suivent des schémas différents ou complexes beaucoup trop difficiles à mapper à des structures relationnelles.

Exemple : modélisation de données XML à l'aide du type de données xml

Prenons l'exemple d'un manuel de produit au format XML. Chaque rubrique fait l'objet d'un chapitre distinct, lui-même composé de plusieurs sections. Une section peut contenir des sous-sections. L'élément <section> est donc un élément récursif. Les manuels de produit regroupent un gros volume de données diverses : contenu, diagrammes, explications techniques ; les données sont semi-structurées. Les utilisateurs veulent pouvoir lancer une recherche contextuelle sur les rubriques qui les intéressent, par exemple rechercher la section consacrée aux « index cluster » dans le chapitre sur l'« indexation », et s'informer des quantités techniques.

Une colonne de type de données xml constitue un modèle de stockage approprié pour vos documents XML. Vous conservez ainsi le contenu InfoSet de vos données XML. L'indexation de la colonne XML permet d'optimiser les performances des requêtes.

Exemple : conservation de copies conformes des données XML

Supposons, par exemple, que la législation en vigueur vous oblige à conserver des copies textuelles conformes de vos documents XML. Il pourrait s'agir notamment de documents signés, de documents juridiques ou d'ordres boursiers. Vous pouvez stocker vos documents dans une colonne [n]varchar(max).

Pour ce qui est des requêtes, convertissez les données en données de type xml lors de l'exécution et appliquez-leur une requête Xquery. La conversion lors de l'exécution peut s'avérer onéreuse, surtout si le document est volumineux. Si vous exécutez fréquemment des requêtes, vous pouvez stocker les documents de façon redondante dans une colonne de type de données xml, puis indexer cette dernière lorsque vous retournez des copies conformes à partir de la colonne [n]varchar(max).

La colonne XML peut être une colonne calculée, basée sur la colonne [n]varchar(max). Toutefois, vous ne pouvez pas créer d'index XML sur une colonne XML calculée, ni créer un index XML à partir de colonnes [n]varchar(max) ou varbinary(max).

Vue XML

En définissant un mappage entre vos schémas XML et les tables d'une base de données, vous créez une « vue XML » de vos données persistantes. Le chargement en bloc XML peut servir à remplir les tables sous-jacentes d'après la vue XML. Vous pouvez aussi interroger la vue XML en utilisant XPath version 1.0 ; la requête est traduite en requêtes SQL portant sur les tables. De même, les mises à jour peuvent se propager à ces tables.

Les vues XML sont utiles dans les cas suivants :

  • Vous voulez avoir un modèle de programmation orienté XML en utilisant des vues XML des données relationnelles existantes.
  • Vous avez un schéma (XSD, XDR) pour vos données XML qu'un partenaire externe vous a fourni.
  • L'ordre de vos données importe peu, les données des tables de requête ne sont pas récursives, ou la profondeur de récursivité maximale est connue à l'avance.
  • Vous voulez interroger et modifier les données via la vue XML en utilisant XPath version 1.0.
  • Vous voulez charger en bloc les données XML, puis les répartir dans les tables sous-jacentes à l'aide de la vue XML.

Il pourrait s'agir de données relationnelles exposées au format XML pour l'échange des données et les services Web, et de données XML avec un schéma fixe. Pour plus d'informations, consultez MSDN Online Library (en anglais).

Exemple : modélisation des données à l'aide d'un schéma XML annoté (AXSD)

Partons du principe que vous disposez de données relationnelles (clients, commandes et articles) et que vous voulez les gérer sous forme XML. Définissez une vue XML en utilisant AXSD sur les données relationnelles. La vue XML vous permet de charger en bloc les données XML dans vos tables, puis d'interroger et de mettre à jour les données relationnelles à l'aide de la vue XML. Ce modèle s'avère très utile si vous avez à échanger des données contenant des balises XML avec d'autres applications alors que les applications SQL fonctionnent sans interruption.

Modèle hybride

Bien souvent, la modélisation des données fait intervenir à la fois des colonnes relationnelles et des colonnes de type xml . Certaines valeurs de vos données XML peuvent être stockées dans des colonnes relationnelles alors que le reste (ou la totalité) des valeurs XML sont stockées dans une colonne XML. Vous obtenez ainsi de meilleures performances puisque vous avez une meilleure maîtrise des index créés sur les colonnes relationnelles et sur les caractéristiques des verrous.

Les valeurs à stocker dans les colonnes relationnelles dépendent de votre charge de travail. Par exemple, si vous récupérez toutes les valeurs XML en fonction de l'expression de chemin, /Customer/@CustId, en promouvant la valeur de l'attribut CustId dans une colonne relationnelle et en l'indexant, vous pouvez accélérer notablement le traitement des requêtes. En revanche, si vos données XML sont largement décomposées de façon non redondante dans des colonnes relationnelles, le réassemblage risque de s'avérer fort coûteux.

Pour des données XML très structurées, par exemple, le contenu d'une table a été converti en XML de façon à pouvoir mapper toutes les valeurs aux colonnes relationnelles, et éventuellement faire appel aux vues XML.

Modélisation des données à l'aide du type de données xml

Cette section explique comment modéliser les données en cas de stockage XML natif. Elle couvre l'indexation des données XML, la promotion des propriétés et le type de données xml typé.

Table identique ou différente

Une colonne de type xml peut être créée dans une table qui contient d'autres colonnes relationnelles ou dans une table distincte dotée d'une relation de clé étrangère avec une table principale.

Créez une colonne de type xml dans la même table si l'une des conditions suivantes est remplie :

  • Votre application récupère les données dans la colonne XML sans demander qu'un index XML existe dans la colonne XML.
  • Vous voulez créer un index XML sur la colonne de type xml et la clé primaire de la table principale est identique à sa clé de cluster. Pour plus d'informations, consultez Indexation d'une colonne de type xml.

Créez la colonne de type xml dans une table distincte si les conditions suivantes sont remplies :

  • Vous voulez créer un index XML sur la colonne de type xml, mais la clé primaire de la table principale est différente de sa clé de cluster, ou la table principale n'a pas de clé primaire, ou la table principale est un segment (sans clé de cluster). Cela peut se produire si la table principale existe déjà.
  • Vous ne voulez pas voir les analyses de la table ralentir suite à la présence d'une colonne XML dans la table. La quantité d'espace utilisée varie selon que le stockage se fait en ligne ou hors ligne.

Granularité des données XML

La granularité des données XML stockées dans une colonne XML est d'une importance capitale pour le verrouillage et de moindre importance pour les mises à jour. SQL Server utilise les mêmes mécanismes de verrouillage, qu'il s'agisse de données XML ou non XML. Par conséquent, le verrouillage au niveau de la ligne verrouille toutes les instances XML de la ligne. En cas de grosse granularité, le verrouillage des grosses instances XML pendant les mises à jour provoque une baisse de la capacité de traitement dans un scénario multi-utilisateur. En cas de décomposition fine, l'encapsulation des objets est perdue et le coût de réassemblage augmente.

Pour le bien de la conception, il est essentiel de trouver un juste équilibre entre les exigences de la modélisation des données et les critères de verrouillage et de mise à jour. Toutefois, dans SQL Server 2005, la taille des instances XML stockées proprement dites ne revêt pas une telle importante.

Par exemple, les mises à jour d'une instance XML s'effectuent à l'aide d'une nouvelle prise en charge des mises à jour partielles d'objets de type BLOB et d'index au cours desquelles l'instance XML stockée est comparée à sa version mise à jour. En cas de mises à jour partielles de grands objets binaires (BLOB), une comparaison différentielle des deux instances XML a lieu et seules les différences sont mises à jour. En cas de mises à jour partielles d'index, seules les lignes devant être changées dans l'index XML sont modifiées.

Type de données xml non typé, typé et contraint

Dans SQL Server 2005, le type de données xml correspond au type de données xml de la norme ISO SQL-2003. Par conséquent, il peut stocker des documents bien formés en conformité avec XML version 1.0, ainsi que des fragments de contenu XML avec des nœuds de texte et un nombre arbitraire d'éléments de premier niveau dans une colonne XML non typée. Le système vérifie que les données sont bien formées, n'exige pas l'association de schémas XML avec la colonne, puis rejette les données mal formées au sens large. Il peut aussi s'agir de variables et de paramètres XML non typés.

S'il existe des schémas XML pour décrire vos données XML, vous pouvez les associer à la colonne XML pour obtenir du code XML typé. Les schémas XML servent à valider les données, à mieux vérifier les types lors de la compilation des instructions de requête et de modification des données qu'en cas de XML non typé, et à optimiser le stockage et le traitement des requêtes.

Utilisez le type de données xml non typé dans les cas suivants :

  • Vous n'avez pas de schéma pour vos données XML.
  • Vous avez des schémas, mais vous ne voulez pas que le serveur valide les données. C'est notamment le cas lorsqu'une application effectue la validation côté client avant de stocker les données sur le serveur, ou stocke temporairement les données XML déclarées non valides par rapport au schéma, ou utilise des composants de schéma non pris en charge par le serveur (key/keyref par exemple).

Utilisez le type de données xml typé dans les cas suivants :

  • Il existe des schémas pour vos données XML et vous souhaitez que le serveur valide les données XML par rapport aux schémas XML.
  • Vous voulez profiter de l'optimisation du stockage et des requêtes que permettent les informations de type.
  • Vous voulez tirer parti des avantages que procurent les informations de type lors de la compilation de vos requêtes.

Des colonnes, paramètres et variables en XML typé peuvent stocker des documents ou du contenu XML. Toutefois, vous devez utiliser un indicateur pour spécifier qu'il s'agit d'un document ou d'un contenu au moment de la déclaration. De plus, vous devez fournir la collection de schémas XML. Spécifiez DOCUMENT si chaque instance XML a un seul et unique élément de premier niveau. Sinon, utilisez CONTENT. Le compilateur de requête utilise l'indicateur DOCUMENT dans les vérifications de type lors de la compilation de la requête pour déduire les éléments singletons de premier niveau.

Outre le typage d'une colonne XML, vous pouvez utiliser des contraintes relationnelles (colonne ou ligne) sur des colonnes de type de données xml typées ou non typées. Utilisez les contraintes dans les cas suivants :

  • Vos règles de gestion ne peuvent pas être exprimées dans les schémas XML. Par exemple, l'adresse de livraison d'un fleuriste doit se trouver à 80 km du magasin. Cela peut faire l'objet d'une contrainte dans la colonne XML. La contrainte peut impliquer des méthodes de type de données xml.
  • Votre contrainte implique d'autres colonnes XML ou non XML de la table. Vous pourriez, par exemple, vouloir absolument que l'ID d'un client (/Customer/@CustId) figurant dans une instance XML corresponde à la valeur d'une colonne relationnelle CustomerID.

Définition de type de document (DTD)

Les colonnes, variables et paramètres de type de données xml peuvent être typés à l'aide d'un schéma XML, mais pas en utilisant DTD. Toutefois, une DTD en ligne peut être utilisée à la fois avec du code XML typé et non typé pour fournir des valeurs par défaut et remplacer des références d'entité par leur forme étendue.

Vous pouvez convertir les DTD en documents de schéma XML à l'aide d'outils tiers, puis charger les schémas XML dans la base de données.

Indexation d'une colonne de type xml

Des index XML peuvent être créés sur des colonnes de type xml. L'indexation porte sur les balises, les valeurs et les chemins rencontrés dans les instances XML de la colonne et contribue à l'optimisation des performances des requêtes. Votre application peut bénéficier d'un index XML dans les situations suivantes :

  • Les requêtes portant sur des colonnes XML sont fréquentes dans votre charge de travail. Le coût de la maintenance des index XML au cours de la modification des données doit être pris en compte lors de l'évaluation des avantages.
  • Vos valeurs XML sont relativement grandes et les parties récupérées relativement petites. En créant un index, vous n'avez plus à analyser l'ensemble des données lors de l'exécution et pouvez profiter de la recherche d'index pour accélérer le traitement des requêtes.

Le premier index d'une colonne XML s'appelle l'index XML primaire. Ensuite, trois types d'index XML secondaires peuvent être créés sur la colonne XML pour accélérer les classes courantes de requêtes, comme l'explique la section suivante.

Index XML primaire

Il indexe toutes les balises, valeurs et chemins rencontrés dans les instances XML d'une colonne XML. La table de base, c'est-à-dire la table dans laquelle se trouve la colonne XML, doit avoir un index cluster placé sur la clé primaire de la table. La clé primaire sert à mettre en corrélation les lignes d'index avec les lignes de la table de base. Des instances XML complètes sont récupérées des colonnes XML, par exemple, SELECT *. Les requêtes utilisent l'index XML primaire et renvoient les valeurs scalaires ou les sous-arborescences XML en se servant de l'index lui-même.

Exemple : création d'un index XML primaire

La table T (pk INT PRIMARY KEY, xCol XML) avec une colonne XML non typée est utilisée dans la plupart des exemples. Cette syntaxe peut très aisément s'adapter à du code XML typé. Pour plus d'informations sur l'utilisation du code XML typé, consultez Type de données xml. Par souci de clarté, les requêtes sont décrites pour des instances de données XML, comme le montre l'exemple qui suit :

<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>

L'instruction suivante crée un index XML, appelé idx_xCol, sur la colonne XML xCol de la table T :

CREATE PRIMARY XML INDEX idx_xCol on T (xCol)

Index XML secondaires

Une fois l'index XML primaire créé, vous pouvez créer des index XML secondaires pour accélérer les différentes classes de requêtes de votre charge de travail. Il existe trois types d'index XML secondaires, PATH, PROPERTY et VALUE dont tirent respectivement parti les requêtes basées sur le chemin, les scénarios personnalisés de gestion des propriétés et les requêtes basées sur les valeurs. L'index PATH construit une arborescence B+ sur la paire (chemin, valeur) de chaque nœud XML dans l'ordre du document pour toutes les instances XML de la colonne. L'index PROPERTY crée une arborescence B+ cluster sur la paire (CP, chemin, valeur) au sein de chaque instance XML, où CP est la clé primaire de la table de base. Pour finir, l'index VALUE construit une arborescence B+ sur la paire (chemin, valeur) de chaque nœud XML dans l'ordre du document pour toutes les instances XML de la colonne.

Voici quelques consignes pour vous aider à créer un ou plusieurs de ces index :

  • Si votre charge de travail utilise souvent des expressions de chemin sur les colonnes XML, l'index secondaire PATH a toutes les chances de l'accélérer. C'est ce que vous pouvez constater le plus souvent en cas d'utilisation de la méthode exist() sur des colonnes XML dans la clause WHERE de Transact-SQL.
  • Si votre charge de travail récupère plusieurs valeurs d'instances XML individuelles en utilisant des expressions de chemin, la mise en cluster des chemins pour chaque instance XML dans l'index PROPERTY peut s'avérer fort utile. Ce scénario se produit généralement avec un sac de propriétés où les propriétés d'un objet sont récupérées et la valeur de sa clé primaire est connue.
  • Si votre charge de travail implique le lancement de requêtes sur des valeurs d'instances XML pour lesquelles vous ne connaissez pas les noms d'élément ou d'attribut, vous aurez peut-être intérêt à créer l'index VALUE. C'est généralement ce qui se produit en cas de recherches d'axes descendants telles que //author[last-name="Howard"], où les éléments <author> peuvent se trouver à tout niveau de la hiérarchie. Cela se rencontre aussi dans les requêtes à caractères génériques telles que /book [@* = "novel"], où la requête recherche les éléments <book> ayant des attributs de valeur "novel".
Exemple : recherche basée sur le chemin

Supposons, par exemple, que la requête suivante fasse souvent partie de votre charge de travail :

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

L'expression de chemin /book/@genre et la valeur "novel" correspondent aux champs clés de l'index PATH. Dans ce cas, l'index XML secondaire de type PATH est d'un grand secours pour cette charge de travail :

CREATE XML INDEX idx_xCol_Path on T (xCol)
   USING XML INDEX idx_xCol FOR PATH
Exemple : récupération des propriétés d'un objet

Étudiez la requête suivante qui récupère les propriétés genre, titre et ISBN d'un livre à partir de chaque ligne de la table 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

L'index PROPERTY s'avère très utile dans ce cas et est créé ainsi :

CREATE XML INDEX idx_xCol_Property on T (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY
Exemple : requête basée sur la valeur

Dans la requête suivante, l'axe descendant-or-self (//) spécifie un chemin partiel de sorte que la recherche basée sur la valeur de ISBN puisse profiter de l'emploi de l'index VALUE :

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

L'index VALUE est créé ainsi :

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

Index de texte intégral sur une colonne XML

Vous pouvez créer un index de texte intégral sur des colonnes XML de façon à indexer le contenu des valeurs XML tout en ignorant le balisage XML. Les index de texte intégral ne peuvent pas être placés sur des valeurs d'attribut (ces dernières faisant partie du balisage) et les balises d'élément sont utilisées comme limites de mot clé. Lorsque cela est possible, vous pouvez associer une recherche de texte intégral et un index XML. Pour cela, procédez comme suit :

  • Tout d'abord, filtrez les valeurs XML pertinentes à l'aide d'une recherche de texte intégral SQL.
  • Ensuite, interrogez les valeurs XML pour lesquelles il existe un index XML sur la colonne XML.
Exemple : association d'une recherche de texte intégral avec une requête XML

Une fois l'index de texte intégral créé sur la colonne XML, la requête suivante recherche une valeur XML contenant le mot « custom » dans le titre d'un livre :

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

La méthode contains() utilise l'index de texte intégral pour créer un sous-ensemble de valeurs XML contenant le mot « custom » à un endroit quelconque du document. La clause exist() vérifie que le mot « custom » apparaît dans le titre d'un livre.

Une recherche de texte intégral qui utilise contains() et la fonction XQuery contains() ont des sémantiques différentes. La dernière cherche à établir une concordance de sous-chaîne tandis que la première fait appel à la correspondance de mot clé avec extraction de radical. Par conséquent, si la recherche porte sur la chaîne où figure « run » dans le titre, les équivalences incluront « run », « runs » et « running » puisque la recherche de texte intégral contains() et la fonction Xquery contains() sont toutes deux satisfaites. En revanche, la requête ne trouve pas le mot « customizable » dans le titre dans la mesure où la recherche de texte intégral contains() échoue et la fonction Xquery contains() est satisfaite. Généralement, pour une pure concordance de sous-chaîne, la clause contains() de texte intégral doit être supprimée.

De plus, la recherche de texte intégral se sert de l'extraction de radical tandis que la fonction XQuery contains() attend une correspondance littérale. Cette différence est expliquée dans l'exemple suivant.

Exemple : recherche de texte intégral sur des valeurs XML à l'aide de l'extraction de radical

La vérification XQuery contains() exécutée dans l'exemple précédent ne peut généralement pas être éliminée. Prenons la requête suivante :

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

Le mot « ran » du document correspond aux critères de recherche du fait de l'extraction de radical. De plus, le contexte de recherche n'est pas vérifié à l'aide de XQuery.

Lorsque le code XML est décomposé, à l'aide de AXSD, dans des colonnes relationnelles indexées en texte intégral, les requêtes XPath qui portent sur la vue XML ne lancent pas une recherche de texte intégral sur les tables sous-jacentes.

Promotion des propriétés

Si les requêtes portent essentiellement sur un petit nombre de valeurs d'élément et d'attribut, vous pouvez promouvoir ces quantités dans les colonnes relationnelles. Cela peut s'avérer utile lorsque les requêtes sont émises sur une petite partie des données XML et que l'ensemble de l'instance XML est récupéré. Il n'est pas nécessaire de créer un index XML sur la colonne XML. En revanche, la colonne promue peut être indexée. Les requêtes doivent être écrites en vue de l'utilisation de la colonne promue afin que l'optimiseur de requête ne redirige plus les requêtes de la colonne XML vers la colonne promue.

La colonne promue peut être une colonne calculée de la même table ou une colonne distincte, gérée par l'utilisateur, d'une autre table. Cela suffit lorsque des valeurs singleton sont promues à partir de chaque instance XML. Toutefois, en cas de propriétés à valeurs multiples, vous devez créer une table distincte pour la propriété, comme l'explique la section suivante.

Colonne calculée basée sur le type de données xml

Une colonne calculée peut être créée à l'aide d'une fonction définie par l'utilisateur qui appelle des méthodes du type de données xml. Le type de la colonne calculée peut être n'importe quel type SQL, y compris XML. L'exemple suivant illustre ce concept.

Exemple : colonne calculée basée sur la méthode du type de données xml

Créez la fonction définie par l'utilisateur pour extraire le numéro ISBN d'un livre :

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

Ajoutez une colonne calculée à la table pour le numéro ISBN :

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

La colonne calculée peut être indexée de manière habituelle.

Exemple : requêtes sur une colonne calculée basée sur les méthodes du type de données xml

Pour obtenir l'élément <book> portant le numéro ISBN 0-7356-1588-2 :

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

La requête portant sur la colonne XML peut être réécrite de façon à utiliser directement la colonne calculée comme suit :

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

Vous pouvez créer une fonction définie par l'utilisateur pour renvoyer le type de données xml et une colonne calculée à l'aide de la fonction définie par l'utilisateur. Toutefois, vous ne pouvez pas créer d'index XML sur la colonne XML calculée.

Création de tables de propriétés

Vous pouvez promouvoir certaines des propriétés à valeurs multiples de vos données XML dans une ou plusieurs tables, placer des index sur ces tables et modifier la cible de vos requêtes de façon à les utiliser. C'est généralement l'attitude à adopter lorsqu'un petit nombre de propriétés couvre la plupart de la charge des requêtes. Vous pouvez effectuer les opérations suivantes :

  • Créez une ou plusieurs tables pour enregistrer les propriétés à valeurs multiples. Vous trouverez plus judicieux de stocker une propriété par table et de dupliquer la clé primaire de la table de base dans les tables de propriétés pour conserver la jointure avec la table de base.
  • Si vous voulez conserver l'ordre relatif des propriétés, vous devez introduire une colonne distincte pour l'ordre relatif.
  • Créez des déclencheurs sur la colonne XML pour assurer la maintenance des tables de propriétés. Dans les déclencheurs, procédez ainsi :
    • Utilisez des méthodes du type de données xml, comme nodes() et value(), pour insérer et supprimer des lignes dans les tables de propriétés.
    • Créez des fonctions table multidiffusion dans CLR (Common Language Runtime) pour insérer et supprimer des lignes dans les tables de propriétés.
    • Écrivez des requêtes qui accèdent par SQL aux tables de propriétés et par XML à la colonne XML de la table de base, et prévoyez des jointures entre les tables à l'aide de leur clé primaire.
Exemple : création d'une table de propriétés

Supposons, dans cet exemple, que vous vouliez promouvoir le prénom des auteurs. Dans la mesure où les livres peuvent avoir un ou plusieurs auteurs, le prénom est une propriété à valeurs multiples. Chaque prénom est stocké dans une ligne distincte d'une table de propriétés. La clé primaire de la table de base est dupliquée dans la table de propriétés pour la jointure en retour avec la table de base.

create table tblPropAuthor (propPK int, propAuthor varchar(max))
Exemple : création d'une fonction définie par l'utilisateur pour générer un ensemble de lignes à partir d'une instance XML

La fonction table suivante, udf_XML2Table, accepte une valeur de clé primaire et une instance XML. Elle récupère le prénom de tous les auteurs des éléments <book> et renvoie un ensemble de lignes pour les paires clé primaire/prénom.

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
Exemple : création de déclencheurs pour remplir une table de propriétés

Le déclencheur insert insère des lignes dans la table de propriétés :

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)

Le déclencheur delete supprime des lignes dans la table de propriétés en fonction de la valeur de la clé primaire des lignes supprimées :

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

Le déclencheur update supprime les lignes existant dans la table de propriétés qui correspondent à l'instance XML mise à jour et insère les nouvelles à la place :

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
Exemple : recherche des instances XML dont les auteurs portent le prénom « David »

La requête peut être formée sur la colonne XML. Une autre possibilité consiste à rechercher le prénom « David » dans la table de propriétés et à faire une jointure en retour avec la table de base pour renvoyer l'instance XML. Par exemple :

SELECT xCol 
FROM     T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE    tblPropAuthor.propAuthor = 'David'
Exemple : solution utilisant la fonction table multidiffusion dans CLR

La solution se compose des étapes suivantes :

  1. Définissez une classe CLR, SqlReaderBase, qui met en œuvre ISqlReader et génère une sortie table multidiffusion par application d'une expression de chemin sur une instance XML.
  2. Créez un assembly et une fonction Transact-SQL définie par l'utilisateur pour démarrer la classe CLR.
  3. Définissez les déclencheurs insert, update et delete à l'aide de la fonction définie par l'utilisateur pour assurer la maintenance de la table de propriétés.

Pour cela, vous devez commencer par créer la fonction multidiffusion dans CLR. Le type de données xml est exposé en tant que classe managée SqlXml dans ADO.NET et prend en charge la méthode CreateReader() qui retourne un flux XmlReader.

ms187508.note(fr-fr,SQL.90).gifRemarque :
L'exemple de code de cette section utilise XPathDocument et XPathNavigator, ce qui vous oblige à charger tous les documents XML en mémoire. Si vous utilisez un code similaire dans votre application pour traiter plusieurs documents XML volumineux, ce code n'est pas évolutif. Au lieu de cela, laissez les allocations de mémoire petites et utilisez les interfaces multidiffusion chaque fois que possible. Pour plus d'informations sur les performances, consultez 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;
   }
}

Ensuite, créez un assembly et une fonction Transact-SQL définie par l'utilisateur SQL_streaming_xml_tvf (non présentée), correspondant à la fonction CLR streaming_xml_tvf. La fonction définie par l'utilisateur sert à définir la fonction table, CLR_udf_XML2Table, pour la génération de l'ensemble de lignes :

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

Pour finir, définissez les déclencheurs en suivant l'exemple « Création de déclencheurs pour remplir une table de propriétés », mais remplacez udf_XML2Table par la fonction CLR_udf_XML2Table. Le déclencheur insert est présenté dans l'exemple suivant :

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)

Le déclencheur delete est identique à celui utilisé dans la version non CLR. Le déclencheur update, quant à lui, remplace simplement la fonction udf_XML2Table() par CLR_udf_XML2Table().

Collections de schémas XML

Une collection de schémas XML est une entité de métadonnées qui est couverte par un schéma relationnel. Elle contient un ou plusieurs schémas XML, qui peuvent être reliés, par exemple par <xs:import>, ou non reliés. Les schémas XML individuels d'une collection de schémas XML sont identifiés à l'aide de leur espace de noms cible.

Pour créer une collection de schémas XML, il faut utiliser la syntaxe CREATE XML SCHEMA COLLECTION (Transact-SQL) et fournir un ou plusieurs schémas XML. Il est possible d'ajouter d'autres composants de schéma XML à un schéma XML existant, et d'autres schémas à une collection de schémas XML à l'aide de la syntaxe ALTER XML SCHEMA COLLECTION (Transact-SQL). À l'instar des autres objets SQL, les collections de schéma XML peuvent être sécurisées à l'aide du modèle de sécurité de SQL Server 2005.

Colonne à plusieurs types

Une collection de schémas XML C type une colonne XML(xCol) selon plusieurs schémas XML. De plus, les indicateurs DOCUMENT et CONTENT indiquent si des arborescences ou des fragments XML peuvent être respectivement stockés dans une colonne xCol.

Avec DOCUMENT, chaque instance XML spécifie l'espace de noms cible de son élément de premier niveau, qui est typé et validé en conséquence. Avec CONTENT, en revanche, chaque élément de premier niveau peut spécifier l'un quelconque des espaces de noms cibles dans C. L'instance XML est validée et typée en fonction de tous les espaces de noms cibles rencontrés dans une instance.

Évolution des schémas

La collection de schémas XML sert à typer les colonnes, les variables et les paramètres XML. Elle permet aussi de faire évoluer le schéma XML. Supposons, par exemple, que vous ajoutiez un schéma XML dont l'espace de noms cible est BOOK-V1 à une collection de schémas XML C. Une colonne XML, xCol typée à l'aide de C, peut stocker des données XML qui respectent le schéma BOOK-V1.

Supposons ensuite qu'une application cherche à étendre le schéma XML avec de nouveaux composants, tels que définitions de type complexes et déclarations d'élément de premier niveau. Ces nouveaux composants de schéma peuvent être ajoutés au schéma BOOK-V1 sans aucune nouvelle validation des données XML existantes de la colonne xCol.

Supposons que l'application veuille ensuite fournir une nouvelle version du schéma XML et qu'elle sélectionne l'espace de noms cible BOOK-V2. Ce schéma XML peut être ajouté à C. La colonne XML peut stocker à la fois des instances de BOOK-V1 et de BOOK-V2, et exécuter des requêtes et des modifications de données sur les instances XML conformes à ces espaces de noms.

Chargement des données XML

Transfert de données XML de SQL Server 2000 vers SQL Server 2005

Vous pouvez transférer des données XML vers SQL Server 2005 de plusieurs manières. Par exemple :

  • Si vos données figurent dans une colonne de type [n]text ou image, dans une base de données SQL Server 2000, vous pouvez importer la table dans une base de données SQL Server 2005 par le biais de SQL Server 2005 Integration Services (SSIS). Modifiez ensuite le type de colonne en XML à l'aide de l'instruction ALTER TABLE.
  • Vous pouvez copier vos données en bloc à partir de SQL Server 2000 à l'aide de bcp out, puis les insérer en bloc dans la base de données SQL Server 2005 à l'aide de bcp in.
  • Si vous avez des données dans des colonnes relationnelles d'une base de données SQL Server 2000, créez une table avec une colonne [n]text et, éventuellement, une colonne de clé primaire pour un identificateur de ligne. Utilisez la programmation côté client pour récupérer les données XML générées sur le serveur par la clause FOR XML et placez-les dans la colonne [n]text. Ensuite, utilisez les techniques citées précédemment pour transférer les données vers une base de données SQL Server 2005. Vous pouvez choisir d'écrire directement le code XML dans une colonne XML de la base de données SQL Server 2005.
Exemple : modification du type de colonne en XML

Supposez que vous voulez modifier le type d'une colonne [n]text ou image, XYZ dans la table R, en type XML non typé. L'instruction suivante se charge de la modification du type :

ALTER TABLE R ALTER COLUMN XYZ XML
  • La cible peut être typée XML, le cas échéant ; il suffit de spécifier une collection de schémas XML.

Chargement en bloc de données XML

Vous pouvez charger en bloc des données XML sur le serveur en utilisant les fonctions de chargement en bloc de SQL Server, comme bcp. OPENROWSET vous permet de charger des données dans une colonne XML à partir de fichiers. L'exemple suivant illustre ce comportement :

Exemple : chargement de données XML à partir de fichiers

Cet exemple montre comment insérer une ligne dans la table T. La valeur de la colonne XML est chargée à partir du fichier C:\MyFile\xmlfile.xml en tant que CLOB, et la colonne integer prend la valeur 10.

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

Codage de texte

SQL Server 2005 stocke les données XML au format Unicode (UTF-16). Les données XML extraites du serveur se présentent au format UTF-16. Si vous souhaitez un codage différent, vous devez convertir les données extraites au format voulu. Il arrive que les données XML soient codées différemment. Si tel est le cas, vous devez prêter une attention particulière au chargement des données. Par exemple :

  • Si votre texte XML est en Unicode (UCS-2, UTF-16), vous pouvez l'affecter à une colonne, une variable ou un paramètre XML sans aucun problème.
  • Si le codage ne se fait pas en Unicode et qu'il est implicite, du fait d'une page de codes source, la page de codes de la chaîne dans la base de données doit être identique (ou du moins compatible) aux points de code que vous souhaitez charger. Si nécessaire, utilisez COLLATE. Si aucune page de codes serveur de la sorte n'existe, vous devez ajouter une déclaration XML explicite mentionnant le codage correct.
  • Pour utiliser un codage explicite, utilisez le type varbinary(), qui n'a aucune interaction avec les pages de codes, ou utilisez un type chaîne de la page de codes appropriée. Ensuite, assignez les données à une colonne, une variable ou un paramètre XML.
Exemple : mention explicite d'un codage

Supposez que vous avez un document XML, vcdoc, stocké au format varchar(max), qui ne comporte aucune déclaration XML explicite. L'instruction ci-dessous permet d'ajouter une déclaration XML mentionnant le codage « iso8859-1 », de concaténer le document XML, de convertir le résultat au format varbinary(max) de façon à conserver la représentation en octets, puis enfin de le convertir au format XML. Ainsi, le processeur XML peut analyser les données conformément au codage spécifié "iso8859-1" et générer la représentation UTF-16 correspondante pour les valeurs de chaîne.

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

XQuery et inférence de type

Le langage XQuery intégré à Transact-SQL est le langage pris en charge pour interroger le type de données xml. Ce langage est en cours de spécification par le World Wide Web Consortium (W3C) où se retrouvent Microsoft et les principaux éditeurs de bases de données. Il utilise XPath version 2.0 comme langage de navigation. Des constructions de langage permettant la modification des données sont également disponibles pour le type de données xml. Pour plus d'informations sur les constructions, les fonctions et les opérateurs XQuery pris en charge dans SQL Server, consultez Fonctions XQuery impliquant le type de données xml.

Modèle d'erreur

Des erreurs de compilation sont renvoyées en cas de syntaxe incorrecte des expressions Xquery et des instructions XML DML. La phase de compilation vérifie l'exactitude des types statiques des expressions XQuery et des instructions DML, et utilise des schémas XML pour valider le code XML typé. Des erreurs de type statique sont générées si une expression risque d'échouer lors de l'exécution suite à la violation de la sécurité du type. Voici quelques exemples d'erreurs statiques : ajout d'une chaîne à un entier ou interrogation d'un nœud inexistant en cas de données typées.

Conformément à la norme du W3C, les erreurs d'exécution XQuery sont converties en séquences vides. Ces séquences peuvent se propager sous forme de valeur XML vide ou NULL dans le résultat des requêtes selon le contexte d'invocation.

Une conversion explicite en type correct permet aux utilisateurs d'éviter les erreurs statiques même si les erreurs de conversion à l'exécution sont transformées en séquences vides.

Les sections suivantes décrivent la vérification du type en détail.

Vérifications des singletons

Les étapes d'emplacement, les paramètres de fonction et les opérateurs qui réclament des singletons renverront une erreur lorsque le compilateur n'arrive pas à savoir si la présence d'un singleton est garantie lors de l'exécution. Ce problème se produit souvent avec des données non typées. Par exemple, la recherche d'un attribut exige un élément parent unique. Un nombre ordinal qui sélectionne un nœud parent unique suffit. L'évaluation d'une combinaison node()-value() pour extraire les valeurs d'attribut ne requiert pas de spécification ordinale, comme le montre l'exemple suivant.

Exemple : singleton connu

Dans cet exemple, la méthode nodes() génère une ligne distincte pour chaque élément <book>. La méthode value() qui est évaluée sur un nœud <book> extrait la valeur de @genre qui, puisqu'il s'agit d'un attribut, est un singleton.

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

Le schéma XML sert à vérifier le type en cas de code XML typé. Si un nœud est spécifié en tant que singleton dans le schéma XML, le compilateur utilise cette information et aucune erreur ne se produit. Dans le cas contraire, un nombre ordinal sélectionnant un nœud unique est requis. En particulier, l'emploi de l'axe descendant-or-self (//), comme dans /book//title, fait perdre l'inférence de cardinalité de singleton de l'élément <title>, même si le schéma XML le déclare en tant que tel. Vous devez par conséquent le réécrire sous la forme (/book//title)[1].

Il faut toujours garder à l'esprit la différence qui existe entre //first-name[1] et (//first-name)[1] en cas de vérification du type. Le premier renvoie une séquence de nœuds <first-name> où chaque nœud est le nœud <first-name> le plus à gauche de ses frères. Le dernier renvoie le premier nœud <first-name> singleton dans l'ordre du document de l'instance XML.

Exemple : utilisation de value()

la requête suivante porte sur une colonne non typée XML et génère une erreur de compilation statique. En effet, value() attend un nœud singleton comme premier argument et le compilateur ne peut pas déterminer si un seul et unique nœud <last-name> sera rencontré lors de l'exécution :

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

L'exemple suivant vous montre une solution à envisager :

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

Toutefois, cette solution ne permet pas de remédier à l'erreur car il peut y avoir plusieurs nœuds <author> dans chaque instance XML. Réécrit ainsi, l'exemple fonctionne :

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

Cette requête renvoie la valeur du premier élément <last-name> de chaque instance XML.

Axe parent

S'il est impossible de déterminer le type d'un nœud, ce dernier devient anyType. Il n'est pas converti implicitement en un autre type. Cette situation se produit le plus souvent lors d'une navigation sur l'axe parent, par exemple, xCol.query('/book/@genre/../price'). Le type du nœud parent est dit anyType dans ce cas. Un élément peut également être défini sous anyType dans un schéma XML. Dans les deux cas, la perte d'une information de type plus précise aboutit à des erreurs de type statique et demande une conversion explicite des valeurs atomiques selon leur propre type.

Accesseurs Data(),text() et string()

XQuery fournit une fonction fn:data() pour extraire, depuis les nœuds, des valeurs typées scalaires, un test de nœud text() pour renvoyer les nœuds de texte, et la fonction fn:string() pour renvoyer la valeur de chaîne d'un nœud. Leur utilisation peut prêter à confusion. Vous trouverez ci-dessous des instructions relatives à leur bonne utilisation dans SQL Server 2005. L'instance XML <age>12</age> est utilisée pour illustrer ce propos.

  • XML non typé : L'expression de chemin /age/text() renvoie le nœud de texte « 12 ». La fonction fn:data(/age) renvoie la valeur de chaîne « 12 » tout comme fn:string(/age).
  • XML typé : L'expression /age/text() renvoie une erreur statique pour tout élément <age> typé simple. fn:data(/age) renvoie l'entier 12 tandis que fn:string(/age) retourne la chaîne « 12 ».

Fonctions et opérateurs sur les types union

La manipulation des types union demande un soin particulier du fait de la vérification du type. Deux des problèmes rencontrés sont expliqués dans les exemples suivants.

Exemple : fonction sur un type union

Considérez une définition d'élément pour <r> d'un type union :

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

Dans le contexte XQuery, la fonction « moyenne » fn:avg (//r) renvoie une erreur statique puisque le compilateur XQuery ne peut pas ajouter des valeurs de types différents (xs:int, xs:float ou xs:double) pour les éléments <r> dans l'argument de fn:avg(). Pour résoudre ce problème, il faut réécrire l'appel à la fonction sous la forme fn:avg(for $r in //r return $r cast as xs:double ?).

Exemple : opérateur sur un type union

L'opération addition (« + ») requiert les types exacts des opérandes. Par conséquent, l'expression (//r)[1] + 1 renvoie une erreur statique qui a la définition de type décrite précédemment pour l'élément <r>. Une solution consiste à la réécrire sous la forme (//r)[1] cast as xs:int? +1, où « ? » indique 0 ou 1 occurrence. SQL Server 2005 requiert « cast as » avec « ? », puisque une conversion peut générer la séquence vide comme résultat des erreurs d'exécution.

Value(), Nodes() et OpenXML()

Vous pouvez utiliser plusieurs méthodes value() sur le type de données xml dans une clause SELECT pour générer un ensemble de lignes pour les valeurs extraites. La méthode nodes() produit une référence interne pour chaque nœud sélectionné en vue d'une requête supplémentaire. La combinaison des méthodes nodes() et value() peut s'avérer plus efficace pour générer l'ensemble de lignes lorsqu'il a plusieurs colonnes et, peut-être, lorsque les expressions de chemin utilisées lors de sa génération sont complexes.

La méthode nodes() fournit des instances d'un type de données xml spécial, chacune ayant son contexte défini sur un nœud sélectionné différent. Ce genre d'instance XML prend en charge les méthodes query(), value(), nodes() et exist(), et peut être utilisée dans les agrégations count(*). Tous les autres emplois génèrent une erreur.

Exemple : utilisation de nodes()

Supposons que vous vouliez extraire les prénoms et les noms des auteurs et que le premier prénom ne soit pas « David ». En outre, vous voulez extraire ces informations sous forme d'un ensemble de lignes composé de deux colonnes, FirstName et LastName. En utilisant les méthodes nodes() et value(), vous pouvez y parvenir en procédant ainsi :

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

Dans cet exemple, nodes('//author') génère un ensemble de lignes de références aux éléments <author> pour chaque instance XML. Les prénoms et les noms des auteurs sont obtenus par l'évaluation des méthodes value() relatives à ces références.

Avec SQL Server 2000, vous avez la possibilité de générer un ensemble de lignes à partir d'une instance XML à l'aide de OpenXml(). Vous pouvez spécifier le schéma relationnel pour l'ensemble de lignes et la façon de mapper les valeurs de l'instance XML avec les colonnes de l'ensemble de lignes.

Exemple : utilisation de OpenXml() sur le type de données xml

La requête peut être réécrite d'après l'exemple précédent en utilisant OpenXml(), comme le montre l'exemple suivant. Vous y parvenez en créant un curseur qui lit chaque instance XML dans une variable XML, et y applique ensuite 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 

La fonction OpenXml() crée une représentation en mémoire et utilise les tables de travail plutôt que le processeur de requêtes. Elle s'appuie ensuite sur le processeur XPath version 1.0 de MSXML version 3.0 au lieu du moteur XQuery. Les tables de travail ne sont plus partagées entre plusieurs appels à OpenXml(), même s'il s'agit de la même instance XML. La montée en charge est ainsi limitée. La fonction OpenXml() vous permet d'accéder à un format de table edge pour les données XML lorsque la clause WITH n'est pas spécifiée. De plus, vous pouvez utiliser la valeur XML restante dans une colonne distincte réservée aux données en excès.

La combinaison des fonctions nodes() et value() tire pleinement parti des index XML. Cette combinaison montre donc une plus grande capacité d'évolution que OpenXml.

Utilisation de FOR XML pour générer du code XML à partir des ensembles de lignes

Vous pouvez générer une instance de type xml à partir d'un ensemble de lignes en utilisant FOR XML avec la nouvelle directive TYPE.

Le résultat peut être assigné à une colonne, une variable ou un paramètre de type xml. De plus, il est possible d'imbriquer des clauses FOR XML pour générer une structure hiérarchique. Les clauses FOR XML imbriquées sont plus faciles à écrire que la clause FOR XML EXPLICIT, mais elles ne s'avèrent pas aussi performantes pour les hiérarchies profondes. FOR XML introduit aussi un nouveau mode PATH qui spécifie le chemin de l'arborescence XML où apparaît la valeur d'une colonne.

La nouvelle directive FOR XML TYPE permet de créer, avec une syntaxe SQL, des vues XML en lecture seule des données relationnelles. La vue peut être interrogée par des instructions SQL et le langage XQuery intégré, comme le montre l'exemple suivant. Vous pouvez également faire référence à ces vues SQL dans les procédures stockées.

Exemple : vue SQL renvoyant un type de données XML généré

La définition de la vue SQL suivante crée une vue XML d'une colonne relationnelle, pk, et extrait les auteurs des livres d'une colonne XML :

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

La vue V contient une seule ligne avec une seule colonne columnxmlVal de type XML. Elle peut faire l'objet d'une requête comme tout autre instance standard de type xml. Par exemple, la requête suivante renvoie l'auteur dont le prénom est « David » :

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

Les définitions de vue SQL s'apparentent un peu aux vues XML créées à l'aide des schémas annotés, bien qu'il y ait des différences de taille. La définition d'une vue SQL est en lecture seule et doit être manipulée avec le langage XQuery intégré. Les vues XML sont créées à l'aide d'un schéma annoté. De plus, la vue SQL matérialise le résultat XML avant l'application de l'expression XQuery tandis que les requêtes XPath sur les vues XML évaluent les requêtes SQL portant sur les tables sous-jacentes.

Ajout d'une logique de gestion

Votre logique de gestion peut être ajoutée aux données XML de plusieurs manières :

  • Vous pouvez écrire des contraintes sur les lignes ou les colonnes de façon à imposer des contraintes propres à un domaine lors de l'insertion et de la modification des données XML.
  • Vous pouvez écrire un déclencheur sur la colonne XML qui se déclenche lors de l'insertion ou de la mise à jour de valeurs dans la colonne. Le déclencheur peut contenir des règles de validation propres au domaine ou remplir les tables de propriétés.
  • Vous pouvez écrire des fonctions SQLCLR en code managé auxquelles vous transmettez des valeurs XML, et utiliser les capacités de traitement fournies par l'espace de noms System.Xml. Vous pouvez, par exemple, appliquer une transformation XSL aux données XML. Vous pouvez également désérialiser le code XML en une ou plusieurs classes managées et travailler sur ces classes à l'aide de code managé.
  • Vous pouvez écrire des procédures stockées et des fonctions Transact-SQL pour lancer le traitement sur la colonne XML en fonction de vos besoins.
Exemple : application de la transformation XSL

Prenez l'exemple d'une fonction CLR TransformXml() qui accepte une instance de type xml et une transformation XSL stockée dans un fichier, applique la transformation aux données XML, puis retourne les données XML transformées dans le résultat. Le code suivant est un squelette de fonction écrit 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);
} 

Après l'enregistrement de l'assembly et la création d'une fonction Transact-SQL définie par l'utilisateur, SqlXslTransform() correspondant à TransformXml(), la fonction peut être appelée à partir de Transact-SQL, comme le montre la requête suivante :

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

Le résultat de la requête contient un ensemble de lignes pour le code XML transformé.

SQLCLR étend les possibilités de décomposition des données XML en tables ou en promotion de propriétés, et d'interrogation des données XML en utilisant les classes managées de l'espace de noms System.Xml. Pour plus d'informations, consultez la documentation en ligne de SQL Server et celle du Kit de développement .NET Framework.

Requêtes inter-domaines

Si vos données résident à la fois dans des colonnes relationnelles et des colonnes de type xml, les requêtes que vous écrivez doivent prévoir le traitement des données relationnelles et XML. Par exemple, vous pouvez convertir les données des colonnes relationnelles et XML en une instance de type xml à l'aide de la clause FOR XML, puis les interroger avec XQuery. À l'inverse, vous pouvez générer un ensemble de lignes à partir de valeurs XML et les interroger à l'aide de Transact-SQL.

Il existe toutefois un moyen plus pratique et plus efficace qui consiste à écrire des requêtes inter-domaines de façon à utiliser la valeur d'une variable ou d'une colonne SQL dans les expressions XQuery ou XML DML :

  • Vous pouvez utiliser sql:variable() pour utiliser la valeur d'une variable SQL dans votre expression XQuery ou XML DML.
  • Vous pouvez utiliser sql:column() pour utiliser les valeurs d'une colonne relationnelle dans votre expression XQuery ou XML DML.

Ces deux approches permettent aux applications de paramétrer les requêtes, comme le montre l'exemple suivant. Toutefois, XML et les types définis par l'utilisateur ne sont autorisés ni dans sql:variable(), ni dans sql:column().

Exemple : requête inter-domaines utilisant sql:variable()

La requête suivante est une version modifiée de l'exemple présenté dans la section « Exemple : requêtes sur une colonne calculée basée sur les méthodes du type de données xml ». Dans la version suivante, ce numéro ISBN spécifique est transmis à l'aide d'une variable SQL @isbn. En remplaçant la constante par sql:variable(), vous pouvez utiliser la requête pour rechercher n'importe quel numéro ISBN et pas seulement le 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() peut être utilisée de manière similaire et apporter d'autres avantages. Pour gagner en efficacité, vous pouvez placer des index sur la colonne, en suivant les suggestions de l'optimiseur de requête basé sur les coûts. De plus, la colonne calculée peut stocker une propriété promue.

Affichages catalogue pour le support XML natif

Les affichages catalogue servent à fournir des informations de métadonnées sur l'utilisation de XML. Certains sont abordés dans la section qui suit.

Index XML

Les entrées d'index XML apparaissent dans l'affichage catalogue, sys.indexes, avec l'index de « type » 3. La colonne name contient le nom de l'index XML.

Les index XML sont également enregistrés dans l'affichage catalogue, sys.xml_indexes, qui contient toutes les colonnes de sys.indexes, ainsi que d'autres plus spécifiques et utiles aux index XML. La valeur NULL de la colonne, secondary_type, indique qu'il s'agit d'un index XML primaire ; les valeurs « P », « R » et « V » représentent respectivement les index XML secondaires PATH, PROPERTY et VALUE.

L'espace utilisé par les index XML figure dans la fonction table sys.dm_db_index_physical_stats, qui fournit des informations telles que le nombre de pages disque occupées, la taille moyenne des lignes en octets et le nombre d'enregistrements, pour tous les types d'index. Vous y trouvez également les index XML. Ces informations sont disponibles pour chaque partition de la base de données. Les index XML utilisent le même schéma et la même fonction de partitionnement que la table de base.

Extraction des collections de schémas XML

Les collections de schémas XML sont répertoriées dans l'affichage catalogue sys.xml_schema_collections. La collection de schémas XML « sys » est définie par le système. Elle contient les espaces de noms prédéfinis qu'il est possible d'utiliser dans toutes les collections de schémas XML définies par l'utilisateur sans avoir à les charger explicitement. Cette liste contient les espaces de noms pour xml, xs, xsi, fn et xdt. Il existe deux autres affichages catalogue : sys.xml_schema_namespaces, qui répertorie tous les espaces de noms de chaque collection de schémas XML, et sys.xml_components, qui répertorie tous les composants de schéma XML de chaque schéma XML.

La fonction intégrée XML_SCHEMA_NAMESPACE, schemaName, XmlSchemacollectionName, namespace-uri, produit une instance de type xml. Cette instance contient des fragments de schéma XML pour les schémas qui sont contenus dans une collection de schémas XML, à l'exception des schémas XML prédéfinis.

Pour répertorier le contenu d'une collection de schémas XML, vous pouvez au choix :

  • écrire des requêtes Transact-SQL sur les affichages catalogue appropriés pour les collections de schémas XML ;
  • utiliser la fonction intégrée XML_SCHEMA_NAMESPACE(). Vous pouvez appliquer les méthodes du type de données xml sur le résultat de cette fonction. En revanche, vous ne pouvez pas modifier les schémas XML sous-jacents.

Ces méthodes sont illustrées dans les exemples ci-après.

Exemple : énumération des espaces de noms XML dans une collection de schémas XML

Utilisez la requête suivante pour la collection de schémas 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'   
Exemple : énumération du contenu d'une collection de schémas XML

L'instruction suivante énumère le contenu de la collection de schémas XML « myCollection » dans le schéma relationnel, dbo.

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

Les schémas XML individuels de la collection peuvent être obtenus sous forme d'instances de type xml en spécifiant l'espace de noms cible comme troisième argument de XML_SCHEMA_NAMESPACE(). Cela est illustré par l'exemple suivant.

Exemple : extraction d'un schéma spécifique d'une collection de schémas XML

L'instruction suivante extrait le schéma XML dont l'espace de noms cible est « https://www.microsoft.com/books » de la collection de schémas XML « myCollection » dans le schéma relationnel, dbo.

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

Interrogation des schémas XML

Vous pouvez interroger les schémas XML que vous avez chargés dans les collections de schémas XML en procédant ainsi :

  • Écrivez des requêtes Transact-SQL sur les affichages catalogue appropriés pour les espaces de noms de schémas XML.
  • Créez une table qui contient une colonne de type xml pour stocker vos schémas XML et aussi les charger dans le système de type XML. Vous pouvez interroger la colonne XML à l'aide des méthodes de type de données xml. Vous pouvez aussi placer un index XML sur cette colonne. Toutefois, dans ce cas, l'application doit assurer la cohérence entre les schémas XML stockés dans la colonne XML et le système de type XML. Par exemple, si vous supprimez l'espace de noms du schéma XML du système de type XML, vous devez aussi le supprimer de la table pour garantir la cohérence.

Voir aussi

Référence

Gestion des collections de schémas XML sur le serveur
Fonctions XQuery impliquant le type de données xml

Concepts

Type de données xml

Autres ressources

sys.dm_db_index_physical_stats
Introduction à la recherche de texte intégral

Aide et Informations

Assistance sur SQL Server 2005