J'ai une table avec 2 noms de noms:
CREATE TABLE Test
(
TestID int identity primary key clustered
, Name_Eng nvarchar(50)
, Name_Nat nvarchar(50)
)
Maintenant, j'ai besoin d'une requête pour obtenir cette colonne Name
séparée par ,
, comme ça:
DECLARE @NameColumns NVARCHAR(1024)
SET @NameColumns = STUFF(
(SELECT ',' + 'Test.' + name AS [text()]
FROM ( SELECT c.name
FROM sys.columns c
INNER JOIN sys.tables t ON t.object_id = c.object_id
WHERE t.name = 'Test'
AND c.name LIKE 'Name_%'
) AS D
FOR XML PATH('') ,
TYPE).value('.[1]', 'VARCHAR(MAX)'), 1, 1,
N'')
select @NameColumns
Mais cette requête a un avertissement dans le plan d'exécution:
Y a-t-il un moyen de supprimer cet avertissement?
L'avertissement est là en raison de la fonction XML value()
. Le deuxième paramètre à value()
est ce que vous voulez que la valeur stockée dans le XML soit convertie en. Vous pouvez faire valoir que ce n'est pas en fait une conversion implicite, mais une conversion très explicite puisque vous demandez que cela se produise. Peut-être quelque chose pour un élément de connexion suggérera-t-il à Microsoft.
Moyen le plus simple de reproduire ce que vous voyez.
declare @X xml;
select @X.value('text()[1]', 'int');
Donne ces deux avertissements.
<Warnings>
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
</Warnings>
Comme vous pouvez le constater que vous l'obtenez également avec Int et vous en obtenez deux pour chaque appel de value()
.
declare @X xml;
select @X.value('text()[1]', 'int'),
@X.value('text()[1]', 'bit');
<Warnings>
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[lvalue],0)" />
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[value],0)" />
</Warnings>
La conversion est effectuée dans l'opérateur d'agrégats de flux qui calculent la valeur comme celle-ci.
MIN(CASE WHEN [@X] IS NULL
THEN NULL
ELSE
CASE WHEN datalength(XML Reader with XPath filter.[value])>=(128)
THEN CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)
ELSE CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)
END
END)
Le résultat de la fonction de valorisation de la table est renvoyé dans la colonne lvalue
ou value
. L'expression vérifie à l'aide de datalength
pour déterminer où il doit être récupéré, puis la convertit à votre type de données souhaité.
Y a-t-il un moyen de supprimer cet avertissement?
Oui il y a. Vous supprimez la directive TYPE
à partir de la relève FOR XML PATH
Et vous supprimez l'appel à la fonction value()
. Un effet secondaire de ce fait est que les valeurs que vous concaténez contiennent des caractères qui doivent être codés dans le XML comme &<>
Seront encodés dans votre résultat.
Bien que je suis d'accord avec @kin sur les types de données, je ne pense pas que cet avertissement soit aussi gênant que vous le pensez. Vous effectuez une concaténation groupée, qui va être des ordres de grandeur plus chères que toutes les conversions de toute façon (et comme Daniel a dit, à moins que vos vues de catalogue soient massives - comme dans la mémoire physique plus grande, il est peu probable que quelque chose n'affecte rien en termes de estimation soit).
J'écrirais la requête de cette façon, en prenant soin de ne jamais utiliser de chaînes de Varchark (qui signifie également arrêter de sortir du préfixe N
préfixe sur les littéraux de chaîne), ainsi que garantissant que vous utilisez des terminaisons d'instruction :
DECLARE @NameColumns nvarchar(max); -- why 1024 when you use max below?
SET @NameColumns = STUFF(
(SELECT N',Test.' + name AS [text()] FROM
(
SELECT c.name FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON t.object_id = c.object_id
WHERE t.name = N'Test'
AND c.name LIKE N'Name_%'
) AS D FOR XML PATH(N''),
TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'');
SELECT @NameColumns;
Même toujours, je ne crois pas qu'il y ait un moyen d'éviter la convertie implicite sans changer la sortie en évitant TYPE
/value()
Comme Mikael suggéré . Je dirais que je m'inquiète de cela lorsque vous pourrez prouver que cela a un impact significatif sur la performance de la requête. Dans mes tests, les deux formes différentes ont effectué la même chose (nous parlons des sous-10ms à chaque fois), mais bien sûr que si j'ai une table nommée Sales & Stuff
Alors il devient Sales & Stuff
Sans la convertir.
L'avertissement dans le plan de requête signifie que, parce que vous avez une conversion de type de données implicite, SQL Server ne pourra pas deviner avec précision le nombre correct de lignes retournées, ce qui est à son tour conduire à un plan moins que optimal.
Ceci est important dans les requêtes qui doivent bien performer, normalement parce qu'elles travaillent avec beaucoup de données, mais dans votre situation, cela ne semble pas être le cas, comme vous pouvez simplement interroger sys.tables
et sys.columns
.
La réponse courte à votre question est la suivante: peu importe que vous avez des millions et des millions de tables et de colonnes dans votre base de données.