Ce sujet n'a pas encore été évalué - Évaluez ce sujet

Optimisation des performances pour le type de données XML

Paru le 13 juin 2005
Par Shankar Pal, Vasili Zolotov et Leo Giakoumakis

Ce document présente plusieurs façons d’améliorer les performances des modifications et des requêtes sur les données XML, dans la version à venir de Microsoft SQL Server 2005. Afin de tirer parti de ce document, une bonne connaissance des fonctionnalités XML dans SQL Server 2005 est nécessaire ; reportez-vous à l’article MSDN XML Support in Microsoft SQL Server 2005 (en anglais) pour une documentation détaillée.

Sur cette page

Introduction
Modélisation des données à l’aide du type de données XML
Structure des données XML
Modification des données et des requêtes

Introduction

Microsoft SQL Server 2005 prend complètement en charge le traitement des données XML. Des données XML peuvent être stockées de façon native dans une colonne de type XML. Elles peuvent être typées selon une collection de schémas XML, ou restées non typées. Une manipulation très fine des données est rendue possible grâce à XQuery, un langage de requête du W3C en passe de devenir un standard, et au langage de modification des données XML. La colonne XML peut être indexée pour améliorer les performances des requêtes. Les applications d’entreprise font de plus en plus appel à XML pour la modélisation de données non structurées et semi structurées, et pourront tirer parti de la prise en charge de XML dans SQL Server 2005.
Ce document fournit des suggestions permettant d’optimiser le stockage, l’interrogation et la modification des données XML. Ces suggestions sont illustrées par des exemples de code. Un article connexe, XML Best Practices for Microsoft SQL Server 2005 (en anglais), fournit des méthodes recommandées pour la modélisation et l’utilisation des données XML. Reportez-vous à Optimizing SQLXML Performance (en anglais) dans MSDN pour l’optimisation de la technologie d’affichage XML à l’aide du mappage.
La suite de cet article s’organise de la façon suivante : nous examinerons les conseils relatifs à la modélisation des données à l’aide de XML, notamment les principes de conception de la base de données, puis nous fournirons des conseils concernant la modification des données et des requêtes afin d’optimiser les performances.

Modélisation des données à l’aide du type de données XML

Les types de données XML fournissent des fonctionnalités de modélisation pour des données semi structurées et non structurées. Les performances du stockage XML et du traitement des requêtes dépendent de la conception de la base de données ainsi que des facteurs tels que la granularité des données XML et l’usage des propriétés à partir des colonnes XML.
La première décision consiste à déterminer si une application a besoin des fonctionnalités du modèle de données XML. Des données structurées se modélisent mieux dans une forme relationnelle, en les plaçant dans des tables et en établissant des relations entre colonnes. Le modèle de données XML est celui qui convient le mieux à vos besoins si vos données doivent conserver l’ordre du document et une structure hiérarchique, et si elles ont une structure récursive.
Il est parfois bénéfique de stocker des données structurées dans une colonne de type de données XML ; par exemple, lorsque les données ont une structure souple ou que cette structure est a priori inconnue.
Ce type d’application correspond à une gestion des propriétés, où les informations de métadonnées relatives aux objets sont modélisées en XML et stockées dans une colonne de type de données XML. Les propriétés les plus souvent interrogées sont promues dans des colonnes de la même table ou d’une table différente. Les propriétés promues peuvent être indexées et interrogées, et les requêtes deviennent plus simples qu’interroger la colonne XML.
Une autre possibilité consiste à décomposer les données XML entrantes sous la forme de tables et de les interroger à l’aide du langage SQL. Si la génération de code XML représente une part importante de la réponse aux requêtes, il n’est pas inutile de stocker une copie redondante des données XML dans une colonne de type XML. Cette copie évite le coût d’exécution associé à la génération du code XML.
Il n’existe aucune règle pour la modélisation des données en XML, et il faut évaluer soigneusement les avantages et les inconvénients de chaque situation de modélisation. Tout aussi important sont le choix entre les colonnes XML typées et non typées, et la manière dont le balisage XML est introduit dans les données. Ces aspects, ainsi que d’autres considérations, sont évoqués dans le reste de cette section.

Structure des données XML

Les mêmes données peuvent être balisées de plusieurs manières (par exemple, d’une manière axée sur les éléments ou sur les attributs, ou une combinaison des deux). Ce choix est dicté par la perception de ce qui constitue le contenu (la valeur de l’élément), les méta-informations (la valeur de l’attribut) et la cardinalité (éléments à occurrences multiples). Introduire un balisage XML dans des données non structurées ou semi-structurées peut être plus efficace que d’autres techniques pour le stockage et le traitement des données.

Utilisation d’un balisage spécifique
Il est parfois pratique d’utiliser des noms d’élément génériques et de distinguer entre différents types d’éléments en utilisant des attributs supplémentaires. Toutefois, cette situation se révèle peu performante dans les requêtes, car elle ne permet pas d’effectuer efficacement les recherches d’index XML. (Reportez-vous à la section Indexation des données XML pour d’autres discussions sur l’indexation XML.)
En revanche, les noms d’éléments nombreux et spécifiques génèrent un balisage plus lisible et permettent de générer des requêtes plus efficaces. Toutefois, un balisage verbeux accroît le coût du stockage. L’exemple suivant illustre ces points.

Exemple : balises génériques et balises spécifiques
Imaginons que vous souhaitiez utiliser le balisage XML pour des informations relatives à un livre et à un DVD. Une fois que vous décidez d’avoir un élément générique appelé <item>, avec un attribut @type affecté de l’une des deux valeurs, « book » et « DVD » pour différencier les deux types d’élément, un livre et un DVD peuvent être représentés de la manière suivante.

<item type="book"><title>Writing Secure Code</title></item> <item type="DVD"><title>The Godfather</title></item>


Les expressions du chemin d’accès pour livre et DVD, respectivement, peuvent s’écrire sous la forme /item[@type = "book"] et /item[@type = "DVD"].
Une autre façon de procéder consisterait à choisir <book> et <DVD> comme balises XML plus directes.

<book><title>Writing Secure Code</title></book> <DVD><title>The Godfather</title></DVD>


Cette représentation simplifie les expressions de chemins d’accès /book et /DVD. Les requêtes sont également plus simples et plus efficaces puisque le prédicat sur l’attribut @type est éliminé.
De plus, elle réduit le nombre de lignes dans l’index XML principal de 4 (1 ligne pour <item>, 1 pour @type et sa valeur, 1 pour <title>, et 1 pour la valeur du titre) à 3 (1 ligne pour <book> ou <dvd>, 1 pour <title>, et 1 pour la valeur du titre).

Balisage centré sur les attributs
La valeur d’un attribut est stockée avec la balise de l’attribut dans une seule ligne de l’index XML principal. Par comparaison, la valeur d’un élément de valeur simple dans du XML non typé est stockée dans une ligne séparée de la balise d’élément. Par conséquent, l’utilisation des valeurs d’attributs dans du XML non typé nécessite un stockage moins important.
De plus, l’évaluation des prédicats est plus efficace, car la valeur de l’attribut s’obtient depuis la même ligne que sa balise dans l’index XML principal, ce qui élimine une jointure. Cette caractéristique est illustrée dans l’exemple suivant.

Exemple : une balise centrée sur les attributs
Dans l’exemple ci-dessus, le titre peut-être modélisé sous la forme d’un attribut plutôt que d’un élément :

<book title="Writing Secure Code"/> <DVD title="The Godfather"/>


Pour un livre, cette représentation réduit le nombre de lignes dans l’index XML principal de 3 (une ligne chacun pour <book>, <title>, et la valeur du titre) à 2 (une ligne pour <book> et une autre ligne pour l’attribut @title). Même situation pour un DVD.
L’expression du chemin d’accès /dvd[title = "The Godfather"] recherche le DVD dont le titre est « The Godfather » avec un balisage centré sur élément pour le titre. Avec le balisage centré sur les attributs, la même requête s’écrit sous la forme suivante /dvd[@title = "The Godfather"], mais elle nécessite une jointure en moins.

XML typé ou non typé
Les valeurs d’attributs et d’éléments dans le code XML non typé (c’est-à-dire, les données XML non décrites par des schémas XML) sont stockées en interne sous la forme de chaînes Unicode. Les opérations sur ces chaînes nécessitent une conversion des données en type approprié. Par exemple, lorsque l’expression du chemin d’accès (/book/price)[1]> 19.99 est évaluée, la valeur de la chaîne de <price> est convertie en nombre décimal pour la comparaison numérique. Un nombre important de ces comparaisons peut devenir coûteux.
Les informations de type fournies par les schémas XML sont utilisées par le moteur de la base de données de plusieurs manières. La conformité aux schémas XML des données XML insérées et mises à jour est vérifiée. Les valeurs d’attributs et d’éléments sont stockées en tant que valeurs typées dans des instances XML, afin que les données stockées soient analysées plus efficacement que la forme textuelle correspondante. Les valeurs typées sont également stockées dans l’index XML. La compilation des requêtes fait appel aux informations de type afin de vérifier la validité du type statique des expressions XQuery et les instructions de modification des données.
L’optimisation des requêtes en fonction de l’inférence du type est également effectuée (par exemple, si <price> de <book> est de type xs:decimal, la conversion de (/book/price)[1] en xs:decimal est éliminée). Cette opération peut avoir un effet positif sur la recherche d’index XML. Un prédicat de plage tel que (/book/price)[1] < 19.99 effectue une analyse de plage sur un index XML secondaire de type VALUE (reportez-vous à la section Indexation des données XML pour des informations sur l’indexation des données XML) ; la conversion des données requise pour du code XML non typé empêchent ces analyses de plage.

Promotion des propriétés
Le modèle de données XML conserve les informations de structure, telles que l’ordre et la hiérarchie du document, au cours du traitement des requêtes. Par conséquent, les requêtes ont tendance à être complexes. Certaines requêtes peuvent se simplifier en promouvant les valeurs scalaires à partir d’une colonne XML vers les colonnes relationnelles de la même table ou d’une table différente. Les propriétés promues peuvent être indexées. La matérialisation et l’indexation des valeurs de propriétés offrent généralement des performances supérieures à l’utilisation de XQuery sur la colonne XML.
La promotion des propriétés améliore les performances lorsque la valeur de propriété est extraite, ou quand la valeur de propriété est utilisée comme filtre pour extraire le blob (objet de grande taille considéré comme binaire) XML correspondant. Dans le dernier cas, la sélectivité de la valeur de propriété est un facteur important.
Les propriétés à valeurs simples peuvent être promues dans les colonnes de la même table en tant que colonnes calculées. Les propriétés à valeurs simples et à valeurs multiples peuvent être promues dans les colonnes d’une table différente et gérées à l’aide de déclencheurs. Ces deux méthodes de promotion des propriétés sont évoquées ci-dessous.

Utilisation des colonnes calculées
Une fonction T-SQL définie par l’utilisateur (UDF) est d’abord créée pour extraire une valeur scalaire à l’aide des méthodes de type de données XML. Une colonne calculée définie par l’UDF est ensuite ajoutée à la table. Ces deux étapes sont répétées pour chaque propriété promue et des index relationnels sont créés sur ces colonnes le cas échéant.
L’expression XQuery sur la colonne XML doit être réécrite pour utiliser les colonnes calculées, et pour les lignes correspondantes, les instances XML sont extraites. Les index des colonnes calculées sont sélectionnés par l’optimiseur. Il en résulte le plus souvent des performances supérieures à l’interrogation de la colonne XML directement, la colonne promue étant précalculée.
L’indexation de la colonne calculée peut être évitée si la colonne est utilisée uniquement dans les listes SELECT et pas pour évaluer des prédicats. Dans certains cas, la persistance de la colonne calculée est suffisante. Lorsque la colonne est indexée, elle doit être persistante si l’expression de colonne calculée est imprécise ou n’est pas déterministe.
L’exemple suivant illustre l’utilisation d’une colonne calculée pour la promotion des propriétés.

Exemple : utilisation d’une colonne calculée pour la promotion des propriétés
Supposons que vous recherchiez généralement des ouvrages à partir de leur numéro ISBN ; la promotion du numéro ISBN dans une colonne calculée est alors judicieuse. Définissez une fonction définie par l’utilisateur pour extraire le numéro ISBN comme suit.

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


Ajoutez une colonne calculée à la table pour ISBN.

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


Créez un index non mis en cluster sur la colonne ISBN.

CREATE INDEX COMPUTED_IDX ON DOCS (ISBN)


Réécrivez votre requête ci-dessous,

SELECT xCol FROM docs WHERE xCol.exist ('/book[@ISBN = "0-2016-3361-2"]') = 1


pour utiliser la colonne calculée comme suit :

SELECT xCol FROM docs WHERE ISBN = '0-2016-3361-2'


La requête réécrite génère un plan de requête plus simple étant donnée que l’extraction du numéro ISBN est précalculée.

Utilisation d’une table de propriétés
Une table de propriétés séparée implique de configurer des déclencheurs insert, delete et update pour assurer la maintenance de cette table. Elle convient aux propriétés à valeurs multiples où chaque ligne de la table des propriétés contient une valeur de propriété (représentation unpivoted). Un exemple qui illustre la création et la maintenance des tables de propriétés se trouve à l’adresse suivante XML Support in Microsoft SQL Server 2005 (en anglais).
Un numéro de séquence est souhaitable si l’ordre relatif des homologues doit être pris en compte dans la table des propriétés. Toutefois, cette situation complique la maintenance de la table des propriétés pour la suppression et l’insertion de sous-arborescence XML.
Il est possible d’ajouter des colonnes de propriétés à valeurs simples à la table pour des raisons pratiques. Cette situation introduit la redondance dans la colonne, mais elle élimine une jointure lorsque deux propriétés sont requises. Si la cardinalité maximale N de la propriété promue est connue de manière anticipée, il peut être pratique de créer N colonnes calculées à la place d’une table de propriétés séparée, et de demander au processeur de requêtes de gérer ces colonnes.

Classement de base de données
Le balisage XML respecte la casse, alors que les comparaisons de chaîne dans les méthodes de type de données XML dépendent de l’organisation de la base de données. Par défaut, le classement de base de données ne respecte pas la casse, ce qui peut être modifié au cours de l’installation de la base de données et ultérieurement à l’aide de l’instruction ALTER DATABASE avec l’option COLLATE. Le classement de l’ensemble du serveur peut être modifié à l’aide de sp_configure ; consultez la documentation en ligne pour savoir quand la modification prend effet.
Si une comparaison respectant la casse est souhaitée dans les instructions de modification des données et des requêtes XML, des valeurs doivent être extraites en tant que types de données binaires (par exemple, varbinary(max)), puis des comparaisons binaires avec les valeurs de recherche doivent être effectuées. Une approche plus rapide et meilleure consiste à faire appel au classement de la base de données pour que les comparaisons respectant la casse soient effectuées par la base de données. S’il n’est pas possible de choisir un classement binaire, vous devez choisir un classement respectant la casse pour la base de données.

Chargement par lots des données XML
Il est possible de charger par lots les données XML dans des colonnes de type de données XML à l’aide des fonctionnalités prévues à cet effet dans SQL Server. Ces fonctionnalités incluent les méthodes BCP IN, BULK INSERT et OPENROWSET.
BCP input a été optimisé pour éviter des copies intermédiaires des données XML autant que possible. Ainsi, en l’absence de contrainte (de ligne ou de colonne) sur la colonne XML, la meilleure solution parmi les trois possibilités est choisie.

Indexation des données XML
Index XML
Pour des requêtes évoluées dans une colonne XML, il est recommandé de créer un index XML principal sur cette colonne. Cet index peut être créé sur une colonne XML typée ou non, et il prend en compte l’ensemble des valeurs et des chemins d’accès sur toute la colonne XML. Il est possible d’améliorer les performances en faisant appel à des index XML secondaires de types PATH, PROPERTY et VALUE.
¤ L’index PATH est utile chaque fois que des expressions de chemin d’accès telles que /book[@ISBN = "0-2016-3361-2"] sont utilisées dans la méthode exist() du type de données XML dans la clause WHERE T-SQL. L’avantage offert est plus net sur de longues expressions de chemins d’accès. L’index PATH fournit une performance sensiblement améliorée.
¤ L’index PROPERTY est utile lorsque plusieurs propriétés d’une instance XML sont extraites dans une instruction SELECT. La mise en grappe des propriétés pour chaque instance XML peut entraîner de meilleures performances.
¤ L’index VALUE est utile pour les expressions de chemin d’accès contenant un axe descendant-or-self (//) et des caractères génériques (par exemple, /book[@* = "novel"]).
L’analyse de la charge des requêtes est nécessaire pour déterminer l’utilité ou non d’un ou de plusieurs index XML secondaires. Le coût de la maintenance de l’index doit être pris en compte lors de l’évaluation des avantages offerts par l’indexation des données XML.
De nombreuses applications connaissent la charge qui sera induite par les requêtes et n’indexeront que les chemins d’accès utilisés dans les requêtes. Ces chemins d’accès peuvent être promus en tant que propriétés comme cela est évoqué dans la section Promotion des propriétés.

Index de texte intégral sur une colonne XML
Les index de texte intégral peuvent être créés sur une colonne XML indépendamment des index XML sur la colonne. Ils ignorent le balisage XML et les valeurs attribute, utilisent des balises d’élément comme limites de mot-clé et indexent le contenu d’élément.
La fonction XQuery fn:contains() possède la sémantique d’une concordance de sous-chaîne tandis qu’une recherche de texte intégral utilisant CONTAINS() fait appel à la correspondance de mot-clé avec recherche de radical. Par conséquent, leurs sémantiques sont différentes. Pour illustrer les différences, une recherche sur le mot « voyage » permet de trouver le mot « voyageur » dans XQuery, mais pas si vous utilisez la sémantique de texte intégral. En revanche, une recherche sur le mot « naviguer » permet de trouver le mot « navigateur » grâce à la sémantique de texte intégral, mais pas dans XQuery. De plus, une recherche de texte intégral ne permet pas de rechercher des valeurs attribute, alors que les expressions XQuery doivent utiliser la fonction d’agrégation fn:string() pour rechercher dans un contenu mixte.
Lorsqu’un index de texte intégral existe sur une colonne XML, il est conseillé de procéder aux opérations suivantes :
1. Filtrer les valeurs XML pertinentes à l’aide d’une recherche de texte intégral.
2. Puis, rechercher les instances XML sélectionnées à l’aide des méthodes de type de données XML. Les index XML sur la colonne XML sont utilisés au cours de cette étape.
Il est ainsi possible d’utiliser à la fois les index XML et de texte intégral. Le choix d’un mot ou d’une expression de recherche très pertinent au cours de la première étape limite davantage le traitement de la deuxième étape à un nombre relativement réduit de lignes dans la table. Il est ainsi possible d’accélérer la requête. Cette approche peut s’utiliser lorsque l’expression de recherche se compose de radicaux qui correspondent aux limites des mots clés.

Isolation de cliché et index XML
La modification des données XML remplace les anciennes instances XML par les instances mises à jour. Ces modifications sont répercutées aux index XML principaux et secondaires. Les lignes modifiées dans la table de base et dans les index XML sont verrouillées, et les verrous de ligne et de page peuvent remonter à la table, à la discrétion de l’optimiseur. Le parallélisme est ralenti par ces verrous, en particulier lorsque des mises à jour sont courantes.
Les isolations de cliché introduisent un nouveau niveau d’isolation nommé « cliché», et une nouvelle mise en œuvre du niveau d’isolation dans SQL Server 2005. Des informations supplémentaires sont disponibles dans la documentation en ligne. Ces isolations reposent sur un mécanisme de version interne qui élimine les conflits de verrous entre les lecteurs et les rédacteurs lorsque la base de données accepte les isolations de clichés. La réduction des conflits de verrouillage s’accompagne d’un meilleur débit.
Une opération de lecture dans les isolations de clichés peut accéder aux données de version sans bloquer une mise à jour concurrente. Cette diminution des verrous est susceptible d’améliorer le débit des transactions dans des tâches en parallèle.
Les valeurs de colonne XML et les lignes d’index XML principal et secondaire correspondantes sont organisées en versions au moment de la mise à niveau. Cette opération évite des versions inutiles lorsque des modifications dans d’autres colonnes entraînent un changement de version de la ligne conteneur. Cette optimisation fait de l’isolation de cliché une fonctionnalité très utile pour le traitement XML.

Modification des données et des requêtes

Utilisation de la méthode exist() pour vérifier l’existence
Pour obtenir de meilleures performances, utilisez la méthode exist() sur des types de données XML autant que possible, plutôt que la méthode value(). La méthode exist() est particulièrement utile lorsqu’elle est employée dans la clause SQL WHERE. Elle utilise les index XML plus efficacement que la méthode value(). Cela reste vrai même lorsque vous utilisez sql:variable() et sql:column() dans des expressions XQuery.
Par exemple, examinez la requête suivante qui extrait les livres portant le titre « Database Theory » à l’aide de la méthode exist().

SELECT * FROM docs WHERE xCol.exist('(/book/title/text())[.="Database Theory"]') = 1


Les index XML secondaires PATH ou VALUE servent pour évaluer l’expression du chemin d’accès (/book[title="Database Theory"]), en incluant les parcours d’index pour des valeurs (« Database Theory » dans cet exemple), afin de produire les instances XML à retourner. Si les valeurs de recherche et du chemin d’accès sont sélectives, l’exécution résultante peut être beaucoup plus rapide qu’évaluer l’expression du chemin d’accès sur l’ensemble des blobs XML dans la colonne.
L’écriture de la requête à l’aide de la fonction value(), comme illustré ci-dessous,

SELECT * FROM docs AND xCol.value('(/book/title)[1]', 'varchar(50)') = 'Database Theory'


évalue d’abord tous les titres de livres, puis applique le filtre « Database Theory ». L’exécution de la requête est moins efficace, car la valeur de filtre « Database Theory » n’est pas utilisée dans une recherche dans un index XML. Une valeur de filtre spécifiée à l’aide d’une variable ou d’une autre méthode value () présente le même comportement.

Agrégation de texte dans du code XML non typé
Selon la sémantique XQuery, une requête comme la requête suivante sur du code XML non typé,

SELECT xCol.value ('(/book/title[.="Database Theory"])[1]', 'nvarchar(64)') FROM docs


ou

SELECT xCol.value ('(/book/title[fn:string()="Database Theory"])[1]'), 'nvarchar(64)') FROM docs


nécessite l’agrégation de l’ensemble des nœuds de texte sous l’élément <title> afin d’évaluer le prédicat. Cela interdit une recherche dans l’index XML de la chaîne de recherche.
Si l’élément <title> ne possède qu’un nœud de texte, une méthode d’écriture plus efficace de la requête consiste à évaluer le prédicat sur le nœud de texte, comme illustré ci-dessous.

SELECT xCol.value ('(/book/title/text())[1][. = "Database Theory"]', 'nvarchar(64)') FROM docs


Une recherche dans l’index XML de la valeur « Database Theory » peut se produire dans ce cas.
Pour le code XML typé, le moteur de la base de données peut effectuer cette inférence à partir du schéma XML associé, et utiliser l’index XML.

Spécification du singleton
Les éléments peuvent être spécifiés en tant que singleton dans les schémas XML avec minOccurs = maxOccurs = 1. Il en résulte une estimation plus exacte de la cardinalité pour les éléments durant l’optimisation des requêtes, et la production d’opérations de jointure efficaces. Cette opération implique généralement d’effectuer les bons choix en ce qui concerne les boucles internes et externes pour les jointures de boucles imbriquées.
Pour les données non typées, il n’y a aucun moyen de spécifier la cardinalité d’un élément. La cardinalité singleton des nœuds peut être indiquée dans les expressions de chemin d’accès à l’aide d’un prédicat ordinal qui choisit exactement un nœud correspondant à l’expression du chemin d’accès. Cette opération correspond à l’utilisation de TOP 1 dans les requêtes T-SQL, elle est illustrée dans l’exemple ci-dessous.
Si le choix d’un nœud unique n’a pas lieu, l’optimiseur de requête utilise une estimation de la cardinalité par défaut qui peut se révéler beaucoup trop élevée et entraîner des choix qui ne sont pas optimisés pour les boucles internes et externes dans les jointures de boucles imbriquées. Cet effet est plus marqué dans le cas de blobs XML ne disposant d’aucun index XML, si bien qu’aucune information de statistique n’est disponible pour une meilleure estimation de la cardinalité.

Exemple : spécification de la cardinalité singleton pour le code XML non typé.
Examinez la requête

SELECT xCol.query ('/book/title') FROM docs


L’optimiseur de requête utilise l’estimation de la cardinalité par défaut pour l’élément <title>. Chaque livre possède un titre unique, si bien que <title> est un élément singleton, mais l’estimation de l’optimiseur est beaucoup plus élevée que cela. La requête reformulée,

SELECT xCol.query ('(/book/title)[1]') FROM docs


traduit la cardinalité correcte à l’optimiseur. La différence entre les expressions de chemin d’accès semblables (/a/b)[1] et /a/b(/book/price)[1] est évoquée dans XML Best Practices for Microsoft SQL Server 2005 (en anglais).

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 porter à confusion. Les instructions relatives à leur bonne utilisation dans SQL Server 2005 sont illustrées ci-dessous à l’aide de l’instance XML <age>12</age>.
1. XML non typé : L’expression de chemin d’accès /age/text() renvoie le nœud suivant dont la valeur est « 12 ». La fonction fn:data(/age) renvoie la valeur de chaîne « 12 », comme fn:string(/age).
2. 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 ».
Ces fonctions ont des caractéristiques de performance différentes. Fn:string() rassemble de manière récursive tous les nœuds de texte sous le nœud de contexte. Cette caractéristique est superflue, lorsque le nœud de contexte a une valeur simple, et fn:data() ou text() serait non seulement suffisant mais également plus efficace. De même, lorsque c’est la valeur d’un nœud qui est souhaitée, fn:data() est plus performant que text().

Paramétrer des expressions XQuery et DML XML
Les expressions XQuery et DML XML ne sont pas autoparamétrées. Par conséquent, si la différence entre deux expressions XQuery ne concerne que les valeurs de leurs paramètres, vous devez utiliser des instructions SQL dynamiques. En revanche, l’utilisation de sql:column() ou sql:variable() pour fournir des valeurs de paramètre à vos expressions XQuery ou DML XML autoparamètre la requête.
L’exemple ci-dessous illustre l’exécution d’une procédure stockée, et la technique peut s’appliquer à l’application de paramètres à une requête, un appel de fonction ou une instruction de modification des données. Par exemple, la procédure stockée ci-dessous recherche des documents qui contiennent une section dont le numéro est spécifié dans l’argument d’entrée.

CREATE PROC sp_myProc @num INT AS SELECT * FROM XmlTable WHERE 1= xCol.exist('(//sec/@secId)[. = sql:variable(@num)])'


Dans ADO.NET et OLEDB, liez la valeur d’entrée de @num à un paramètre. Vous évitez ainsi une recompilation de la requête lorsque le paramètre est lié à une valeur différente. L’utilisation de sql:column() offre des avantages similaires.
Le code Visual Basic.NET suivant illustre des liaisons de paramètres dans l’invocation de la procédure stockée.

'myConn is the connection string SqlCommand cmd = New
SqlCommand("sp_myProc", myConn) cmd.CommandType =
CommandType.StoredProcedure 'Parameter binding Dim myParm As SqlParameter =
cmd.Parameters.Add("@num", SqlDbType.Int) myParm.Direction =
ParameterDirection.Input myParm.value = 2 'Invoke the stored procedure
SqlDataReader myReader = cmd.ExecuteReader() 'Invoke the stored procedure a second time myParm.value = 4
SqlDataReader myReader = cmd.ExecuteReader()

Pour plus d’informations, reportez-vous à la documentation Microsoft Visual Studio.NET (en anglais).

Suppression des prédicats du milieu des expressions de chemin d’accès
Les chemins d’accès complets (c’est-à-dire les chemins d’accès en absolu, du nœud racine aux nœuds sélectionnés, contenant uniquement des axes enfants et des axes self) sans test de nœud ou de branchement (à savoir, des prédicats sur des nœuds intermédiaires dans le chemin d’accès) peuvent être évalués d’une manière efficace. Dans le cas indexé, les chemins d’accès complets peuvent être utilisés dans les recherches d’index. Pour un blob XML, l’analyse est plus rapide que les autres chemins d’accès tels que les chemins disposant de branchement, de caractères génériques (*) ou d’un axe descendant-or-self
Les tests de nœuds et les prédicats en fin d’un chemin d’accès complet sont utilisés en tant que filtre sur les nœuds sélectionnés. Les index sont utilisés, et l’analyse de blob est efficace. Voici un exemple ci-dessous.

Exemple : évaluation de chemin d’accès complet
Considérons l’expression de chemin d’accès qui sélectionne des livres rédigés par des auteurs dont le prénom est Davis.

SELECT xCol.query ('/book[author/first-name = "Davis"]') FROM docs


Même si le prédicat ne figure pas directement sur les éléments <book>, les nœuds <first-name> localisés à l’aide du chemin d’accès complet /book/author/first-name sont filtrés par la valeur « Davis ». Les éléments <book> sont recherchés en tant que grand-parent des éléments <first-name> éligibles.
La recherche basée sur le chemin d’accès est moins efficace lorsque des chemins d’accès partiels sont spécifiés, tels que /book//first-name. Les éléments <book> et <first-name> sont localisés séparément, et leurs relations ancêtre-descendant sont vérifiées à l’aide de leur numéro de nœud interne. Ainsi, le fait de détailler le plus possible le chemin d’accès contribue à un traitement plus efficace.
Le branchement (c’est-à-dire, les tests de nœuds et les prédicats au milieu de l’expression du chemin d’accès), comme dans /book[@ISBN = "1-8610-0157-6"]/author/first-name évalue les expressions de chemin d’accès /book[@ISBN = "1-8610-0157-6"] et /book/author/first-name, et scinde les deux jeux des éléments <book> . Par conséquent, l’exécution est plus lente que les expressions de chemin d’accès sans branchement. L’utilisation des tests de nœuds et des prédicats au milieu des expressions de chemin d’accès doit être évitée autant que possible. Cela est parfois possible avec une modélisation minutieuse des données, comme indiqué dans l’exemple Utilisation d'un balisage spécifique.

Conditions d’étendue
Les conditions d’étendue bénéficient de l’utilisation de code XML typé. Les données stockées dans les colonnes et les index XML sont typées en fonction des types spécifiés dans les schémas XML. Les comparaisons de valeur évitent la conversion de données lors de l’exécution et autorisent les analyses d’étendue à l’aide de valeurs sur l’index XML secondaire VALUE. Elles nécessitent également de spécifier le nœud de context (.) dans la condition de plage, comme illustré par l’exemple suivant.

Exemple : nœud de contexte dans des conditions d’étendue
Examinez la requête destinée à recherches des livres dont les prix se situent dans la gamme $9.99 et $29.99.

SELECT xCol FROM T WHERE xCol.exist ('/book[price > 9.99 and price < 29.99]') = 1


Les expressions de chemin d’accès /book/price > 9.99 et /book/price < 29.99 sont évaluées séparément. L’optimiseur de requête ignore que les éléments <price> sont les mêmes, ce qui limite les analyses d’étendue sur l’index XML secondaire VALUE. La réécriture proposée ci-dessous permet d’utiliser le même nœud de contexte, et les analyses d’étendue de l’index XML secondaire VALUE se produisent pour les valeurs comprises entre 9.99 et 29.99. Cette requête offre de meilleures performances.

SELECT xCol FROM T WHERE xCol.exist ('/book/price[. > 9.99 and . < 29.99]') = 1


Interrogation dynamique
Les expression XQuery sont spécifiées sous la forme de littéraux dans les méthodes de type de données XML. Leur évaluation utilise des index XML en fonction de leur disponibilité, et de leur sélection par l’optimiseur de requête.
Le développement d’applications est simplifié lorsque des expressions XQuery peuvent être dynamiquement spécifiées à la place de littéraux. C’est possible dans les cas suivants :
¤ Construction de requêtes
Créez la requête sous la forme d’une chaîne, et utilisez sp_executesql pour son exécution. Contrairement à EXEC, cette formule met en cache la requête compilée, et l’optimiseur peut la réutiliser. La requête peut être paramétrée puisqu’elle existe sous forme de chaîne et peut contenir des paramètres incorporés.
¤ Utilisation des fonctions XPath
Remplacez chaque niveau dans une expression XPath par une fonction name() ou par des fonctions local-name() et namespace-URI(). Il en résulte une requête à laquelle vous pouvez passer des noms de nœuds et des valeurs de recherches. Vous pouvez affiner le paramétrage comme cela est décrit dans la section Paramétrer des expressions XQuery et DML XML. Ces requêtes paramétrées sont utiles pour le développement d’applications, mais la requête générée ignore les index XML puisque les chemins d’accès spécifiques ne sont pas connus au moment de la compilation.

Même si la construction des requêtes offre de meilleures performances grâce à ces deux approches, elle comprend le coût de la compilation des requêtes à l’exécution, ce qui rend l’opération plus lente que de spécifier la requête complète en tant que littéral. La requête proprement dite qui est passée par l’utilisateur doit être validée pour éviter les menaces sur la sécurité issues des attaques de type injection SQL ; sinon cette approche doit être évitée en faveur de l’écriture avec paramètres de la requête (reportez-vous à la section Paramétrer des expressions XQuery et DML XML). L’exemple suivant illustre cette approche.
La deuxième approche qui spécifie les tests de nœuds à l’aide des noms de nœuds évite le problème de l’injection SQL. Toutefois, la requête se complique et connaît des performances bien inférieures à la requête d’origine. Cette requête est illustrée dans le second exemple qui suit.

Exemple : requête utilisant sp_executesql
Supposons que vous souhaitiez créer la requête suivante d’une manière dynamique, et passer la valeur de recherche de @genre à l’aide d’un paramètre.

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


Il est possible de créer la requête dynamique et de l’exécuter comme indiqué ci-dessous. La chaîne de requête est créée dans la variable @SQLString, et contient une variable incorporée @bksubj utilisée dans la méthode exist(). La variable @subj fournit la valeur au moment de l’exécution du paramètre. La requête dynamique qui est passée à l’aide de @SQLString doit être validée (non montré ici) pour éviter les attaques de type injection SQL.

DECLARE @SQLString NVARCHAR(500) DECLARE @subj NVARCHAR(64) DECLARE @ParmDefinition NVARCHAR(500) --- Build the SQL string once SET @SQLString = N'SELECT * FROM docs WHERE
xCol.exist(''/book[@subject=sql:variable("@bksubj")]'')=1' SET @ParmDefinition = N'@bksubj NVARCHAR(64)' --- Execute the string with the first parameter value SET @subj = 'security' EXECUTE sp_executesql @SQLString, @ParmDefinition, @bksubj = @subj


Exemple : requête utilisant local-name()
Supposons que vous ayez écrit la requête

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


Elle peut être réécrite en utilisant des noms de balises en tant que littéraux, comme suit

SELECT * FROM T WHERE xCol.exist('/*[local-name() = "book" and  @*[local-name() = "genre" and . = "novel"]]') = 1


La requête réécrite contient des caractères génériques et des tests de nœuds qui utilisent des noms de nœuds, et elle est difficile à optimiser. Par conséquent, ses performances sont nettement inférieures à la requête d’origine.

Axe parent
Le fait d’accéder au parent d’un nœud entraîne une perte des informations de type et retourne un nœud anyType. D’autres opérations sur le nœud peuvent nécessiter un cast explicite, et ralentir le traitement des requêtes (par exemple, le cast peut empêcher l’utilisation de l’index). Il est avantageux de descendre depuis le nœud parent plutôt que de remonter vers le parent.
Génération d’ensemble de lignes à partir de données XML
Certaines applications doivent générer un ensemble de lignes à partir des données XML en promouvant une ou plusieurs propriétés dans les colonnes de l’ensemble de lignes. Par exemple, une application peut interroger les auteurs des livres et afficher le résultat sous la forme d’une table contenant deux colonnes pour les premiers et derniers noms. Cette génération d’un ensemble de lignes peut s’effectuer aussi bien au niveau du serveur que du client, avec des caractéristiques de performance différentes :

¤ Au niveau du serveur, utilisez les mécanismes suivants :
1. Une combinaison des méthodes nodes() et value() sur le type de données XML
2. OpenXML
3. La fonction table multi-diffusion dans CLR
¤ Le résultat XML est également retourné au client qui utilise la programmation côté client (par exemple, DataSet) pour convertir les données en un ensemble de lignes.
La génération de l’ensemble de lignes côté client décharge le serveur, et s’avère utile lorsque la majorité des données envoyée du serveur au client est mappée dans l’ensemble de lignes. Sinon, le coût de l’envoi des données peut être supérieur aux avantages offerts par le traitement côté client.
La génération d’ensemble de lignes côté serveur est pratique pour générer un ensemble de lignes à partir de données XML entrantes au niveau du serveur. Elle est généralement préférable lorsqu’une petite portion de données XML stockées au niveau du serveur est promue dans des colonnes de l’ensemble de lignes. D’autres discussions concernant les avantages et les inconvénients des traitements coté serveur sont disponibles dans XML Best Practices for Microsoft SQL Server 2005 (en anglais).
Cela vous a-t-il été utile ?
(1500 caractères restants)
© 2013 Microsoft. Tous droits réservés.