J'ai une variable xml qui contient un set d'identifiants que je veux searchr dans une table. Lors de l'interrogation, j'ai essayé plusieurs versions, mais ce qui suit (de mes tests) semble être le plus rapide:
declare @idsxml as xml (IdSchemaColelction) = '<root><Id>505766</Id><Id>458073</Id><Id>460689</Id><Id>464050</Id></root>' SELECT * FROM entity WHERE @idsXml.exist('/root/Id[data(.)=sql:column("id")]') = 1
Le problème est que le plan de requête a l'avertissement suivant "Type de conversion dans expression (CONVERT_IMPLICIT (sql_variant, CONVERT_IMPLICIT (numérique (38,10), [xmlTest]. [Dbo]. [Entité]. [Id], 0), 0 )) peut affecter "CardinalityEstimate" dans le choix du plan de requête "
J'ai créé un schéma XML qui définit le text de Id comme étant un entier, donc je m'attendrais à ce que data(.)=sql:column("id")
soit une comparaison entre des entiers mais cet avertissement suggère le contraire.
Quelle est la bonne façon de supprimer cet avertissement dans ce cas? Cela a-t-il des répercussions sur les performances?
Définition du tableau et définition du schéma:
CREATE XML SCHEMA COLLECTION IdSchemaColelction AS ' <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes" > <xs:import namespace="http://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="http://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd"/> <xs:element name="root"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="Id" type="sqltypes:int" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> ' go create table entity ( id int not null primary key)
Je ne pense pas, que votre approche soit la meilleure ou la plus rapide …
Voici quelques approches pour les comparer:
Utilisez ceci pour les tests
create table test ( id int not null primary key); insert into test VALUES(100),(200),(505766),(300),(400),(500),(458073),(600),(700),(464050),(800),(900),(1000) GO
Voici votre list d'identifiants
declare @idsxml as xml = '<root><Id>505766</Id><Id>458073</Id><Id>460689</Id><Id>464050</Id></root>'
-C'est votre approche. Il parsingra le XML encore et encore – .data()
est – à coup sûr – pas la meilleure façon de lire datatables de type de security pour la comparaison …
SELECT test.id FROM test WHERE @idsXml.exist('/root/Id[data(.)=sql:column("id")]') = 1;
-C'est exactement la même approche mais avec un XQuery plus rapide
SELECT test.id FROM test WHERE @idsXml.exist('/root/Id[text()=sql:column("id")]') = 1;
–Ceci est un peu plus lent … Probablement parce qu'il y a une conversion de type implicite …
SELECT test.id FROM test WHERE @idsXml.exist('/root[Id=sql:column("id")]') = 1;
– Avec une list plus grande, il pourrait être préférable d'utiliser une table dérivée dans un INNER JOIN
WITH DerivedTable AS ( SELECT i.value('.','int') AS id FROM @idsxml.nodes('root/Id') AS A(i) ) SELECT test.id FROM test INNER JOIN DerivedTable AS dt ON test.id=dt.id;
– Et avec une grande list, vous pourriez même penser à une table déclarée indexée (lire environ in memory
pour la dernière vitesse quantique)
DECLARE @tbl TABLE(id INT NOT NULL PRIMARY KEY) --PK only, if your XML never contains a value twice! INSERT INTO @tbl SELECT i.value('.','int') AS id FROM @idsxml.nodes('root/Id') AS A(i); SELECT test.id FROM test INNER JOIN @tbl AS tbl ON test.id=tbl.id; GO DROP TABLE test;