Je viens de faire un petit test sur les colonnes XML
On se doute tous que l’ajout d’index sur une colonne XML peut améliorer les performances, pour peu que bien sur vous ayez besoin de requêter la colonne XML elle même que ce soit via un PATH ou une VALUE (Xquery power)
Si ce n’est pas le cas, un bon vieux VarChar(max) est bien plus performant. N’oubliez pas qu’une colonne xml est en fait une bonne vielle table stockée dans une table système de votre base
Deuxième chose, la performance d’une colonne XML soumise à un schéma de validation XSD.
ON sait qu’un schéma va permettre de valider les données contenu dans notre champ XML. Déjà rien que ça justifie pleinement le fait de mettre une colonne XML Validée par un schéma XSD
Pour notre test, j’ai créé un schéma XSD, qui me permettra de valider ma colonne XML :
-- Création du schémaCREATE XML SCHEMA COLLECTION AdditionalInfos AS '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:simpleType name="addressType"><xs:restriction base="xs:string"><xs:enumeration value="Home" /><xs:enumeration value="Office" /><xs:enumeration value="Travel" /><xs:enumeration value="Undefined" /></xs:restriction></xs:simpleType><xs:element name="additionalOrder"><xs:complexType><xs:sequence><xs:element name="orderDate" type="xs:dateTime"/><xs:element name="additionalAddress" maxOccurs="unbounded"><xs:complexType><xs:sequence><xs:element name="name" type="xs:string"/><xs:element name="address" type="xs:string"/><xs:element name="city" type="xs:string"/><xs:element name="country" type="xs:string"/></xs:sequence><xs:attribute name="name" type="xs:string" use="required"/><xs:attribute name="type" type="addressType" use="required"/></xs:complexType></xs:element><xs:element name="complementOrder" maxOccurs="unbounded"><xs:complexType><xs:sequence><xs:element name="name" type="xs:string" /><xs:element name="quantity" type="xs:positiveInteger" /><xs:element name="price" type="xs:decimal"/></xs:sequence><xs:attribute name="orderId" type="xs:positiveInteger" use="required"/></xs:complexType></xs:element></xs:sequence><xs:attribute name="additionalOrderId" type="xs:positiveInteger" use="required"/></xs:complexType></xs:element></xs:schema>'
Puis j’ai créé mes colonnes XML
- Première colonne : Colonne XML validée par mon schéma avec un index dessus et 2 index supplémentaires (VALUE et PATH)
- Deuxième colonne : Colonne XML sans schéma avec un index dessus et 2 index supplémentaires (VALUE et PATH)
- Troisième colonne : Colonne XML sans schéma sans index
Voilà la définition de ces colonnes:
-- Modification de la tableALTER TABLE Sales.Customer ADDAdditionnalInformations xml(DOCUMENT dbo.AdditionalInfos) NULLGO-- Création de l'index primaire XMLCREATE PRIMARY XML INDEX PXML_AdditionnalInformations on Sales.Customer (AdditionnalInformations)-- Création d'un index secondaire de type PathCREATE XML INDEX PXML_AdditionnalInformations_Path ON Sales.Customer (AdditionnalInformations)USING XML INDEX PXML_AdditionnalInformationsFOR PATH-- Création d'un index secondaire de type ValueCREATE XML INDEX PXML_AdditionnalInformations_Value ON Sales.Customer (AdditionnalInformations)USING XML INDEX PXML_AdditionnalInformationsFOR VALUE-- Modification de la tableALTER TABLE Sales.Customer ADD AdditionnalInformationsWithoutSchema xml NULLGO-- Création de l'index primaire XMLCREATE PRIMARY XML INDEX PXML_AdditionnalInformations_WithoutSchema on Sales.Customer (AdditionnalInformationsWithoutSchema)-- Création d'un index secondaire de type PathCREATE XML INDEX PXML_AdditionnalInformations_WithoutSchema_Path ON Sales.Customer (AdditionnalInformationsWithoutSchema)USING XML INDEX PXML_AdditionnalInformations_WithoutSchemaFOR PATH-- Création d'un index secondaire de type ValueCREATE XML INDEX PXML_AdditionnalInformations_WithoutSchema_Value ON Sales.Customer (AdditionnalInformationsWithoutSchema)USING XML INDEX PXML_AdditionnalInformations_WithoutSchemaFOR VALUEALTER TABLE Sales.Customer ADDAdditionnalInformationsWithoutSchemaWithoutIndexes xml NULL
Y’a plus qu’à remplir les données, via un bon vieil INSERT ou UPDATE
Vous trouverez le script en PJ pour vous faire une idée
Voici les résultats de l’insertion
| Colonne XML Index + Schéma | 02:51 |
| Colonne XML Index | 03:56 |
| Colonne XML | 00:08 |
Alors oui, on est d’accord, l’insertion est bien plus performante sur une colonne XML sans index, un Blob quoi.
On Imagine bien que le parsing de chaque valeur XML et la création d’une table système avec une insertion à la volée peut être consommateur de ressources !
Passons aux requêtes de Sélection sur une requête Xquery PATH
-- Using PATH indexselect Top 10 C.CustomerID, C.AdditionnalInformations.query('/additionalOrder/additionalAddress[@type = "Home"]')from Sales.Customer Cselect Top 10 C.CustomerID, C.AdditionnalInformationsWithoutSchema.query('/additionalOrder/additionalAddress[@type = "Home"]')from Sales.Customer Cselect Top 10 C.CustomerID, C.AdditionnalInformationsWithoutSchemaWithoutIndexes.query('/additionalOrder/additionalAddress[@type = "Home"]')from Sales.Customer C
Je ne mesure pas le temps ici, il faudrait avoir un champ XML totalement énorme pour avoir une différence notable. J’ai donc noté le % d’exécution donné par le plan d’exécution
| Colonne XML Index + Schéma | 0% |
| Colonne XML Index | 0% |
| Colonne XML | 100% |
Bon Ok, la requête sur le BLOB sans index écroule tout. Je refais le test uniquement sur les colonnes indexés :
| Colonne XML Index + Schéma | 49% |
| Colonne XML Index | 51% |
Bon le gain sur une requête PATH n’est pas notable qu’on soit ou non validé par un schéma
Passons aux requêtes de Sélection sur une requête XQuery VALUE
-- Using VALUE indexSelect C.CustomerID, C.AdditionnalInformationsfrom Sales.Customer Cwhere C.AdditionnalInformations.exist('/additionalOrder/@additionalOrderId[.=100]') = 1Select C.CustomerID, C.AdditionnalInformationsfrom Sales.Customer Cwhere C.AdditionnalInformationsWithoutSchema.exist('/additionalOrder/@additionalOrderId[.=100]') = 1Select C.CustomerID, C.AdditionnalInformationsfrom Sales.Customer Cwhere C.AdditionnalInformationsWithoutSchemaWithoutIndexes.exist('/additionalOrder/@additionalOrderId[.=100]') = 1
Les premiers résultats en comparant avec une colonne non indexée sont identiques à l’exemple précédent. Je passe donc directement à la comparaison des deux colonnes indexées :
| Colonne XML Index + Schéma | 0% |
| Colonne XML Index | 100% |
Wow … là oui y’a différence !
Examinons le plan d’éxécution :
Que se passe t’il : dans le premier cas, on a un schéma, donc une table fortement typée, où SQL SERVER sait que quelque soit la ligne, les données (enfin le type) sera identique :
Il peut donc faire une requête directement sur l’index
Sur la deuxième requête, la colonne est “permissive”. On peut stocker n’importe quel champ xml.
Pour pouvoir récupérer toutes les valeurs ‘@type’ il est donc obligé de faire un bon vieux gros SCAN de l’index XML PRIMARY
D’où l’intérêt du schéma !
Conclusion :
Les schémas c’est bien, une colonne XML indexé avec un schéma c’est performant et secure.
Par contre, vous perdez en Insertion. Attention à l’intéret d’une colonne XML si vous ne requêtez jamais l’arbre XML !
Quelques liens utiles :
- http://msdn.microsoft.com/en-us/library/ms345121(SQL.90).aspx
- http://msdn.microsoft.com/en-us/library/ms184277.aspx
- http://technet.microsoft.com/fr-fr/library/ms345118(SQL.90).aspx
Voilà voilà, bon indexation, bon XML tout ça tout ça !